How to draw a one-pixel line??

L

Loren Pechtel

I'm having trouble with what seems to be a simple task:

I'm trying to draw some vector images on a field of tiles. The tiles
are always of integer sizes and can zoom over any reasonable range of
values.

The images themselves are vector images but they don't simply scale
up, as the tile gets bigger more detail is added. (Yeah, I know I'll
have to handle printing a bit differently.)

The problem I'm having is with images composed entirely of vertical
and horizontal lines, black on a gray field.

The horizontal lines are fine, I get exactly what I want. Vertical
lines aren't working too well, though. A lone vertical line is good
enough--two pixels of dark gray instead of one of black but it looks
close enough I didn't even realize it was wrong until I got digging
into the case that's awful:

I have some stripes, some are black/background/background and others
are black/background.

With antialiasing on the ones with the black every three look fuzzy
and the closer ones just look like a blurry blob.

With it turned off I'm getting a very clear image, albeit very wrong:
The three-step one produces dark gray/dark gray/background and the
two-step one simply produces a box of dark gray.

Everything is being drawn on a bitmap that is then put in a
picturebox. The picturebox is in a panel that fills the form other
than a few controls on the right for manipulating the view.

The behavior would make sense if somehow I was drawing between pixels
on the y axis but all the DrawLines are being fed integer coordinates.
 
P

Peter Duniho

Loren said:
[...]
The behavior would make sense if somehow I was drawing between pixels
on the y axis but all the DrawLines are being fed integer coordinates.

With no scaling/transformation whatsoever? And you are displaying the
images also without any scaling or transformation of any sort? How are
you determining what the exact pixel values are?

Your description sounds as though you're drawing on fractional
coordinates, in spite of your statement that you're not. But it's also
possible you're simply not gathering your data correctly and that the
pixels in the bitmap aren't what you think.

Without a concise-but-complete code example that reliably demonstrates
the problem, it's practically impossible to understand for sure what
problem you're seeing, never mind know what the fix would be.

For what it's worth, I tried to reproduce the issue you seem to be
describing, and couldn't. When I start with an all-gray bitmap and draw
vertical lines into it spaced at various intervals, I always get pixels
exactly the color I drew where the lines are, with the original
background color where no lines were drawn. See below for a
concise(ish)-but-complete code example demonstrating my test.

Pete


using System;
using System.Windows.Forms;
using System.Text;
using System.Drawing;

namespace TestVerticalLineDrawingSingleFile
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}

public class Form1 : Form
{
private Image _img;
private Timer _timerBitmap = new Timer();
private Timer _timerRGB = new Timer();

public Form1()
{
InitializeComponent();

_UpdateImage();
_UpdatePixelRGB();

_timerBitmap.Interval = 1000;
_timerBitmap.Tick += (sender, e) =>
{
_timerBitmap.Enabled = false;
_UpdateImage();
};

_timerRGB.Interval = 1500;
_timerRGB.Tick += (sender, e) =>
{
_timerRGB.Enabled = false;
_UpdatePixelRGB();
};

textBox1.TextChanged += (sender, obj) =>
_ResetTimer(_timerBitmap);
textBox2.TextChanged += (sender, obj) =>
_ResetTimer(_timerBitmap);
textBox3.TextChanged += (sender, obj) =>
_ResetTimer(_timerRGB);
}

private void _UpdateImage()
{
Image imgNew = new Bitmap(128, 128);

using (Graphics gfx = Graphics.FromImage(imgNew))
{
int dx;

if (!int.TryParse(textBox1.Text, out dx))
{
dx = 1;
textBox1.Text = dx.ToString();
}

//gfx.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.None;
gfx.Clear(Color.Gray);
for (int x = 0; x < imgNew.Width; x += dx)
{
gfx.DrawLine(Pens.Black, x, 0, x, imgNew.Height);
}
}

pictureBox1.Image = imgNew;
if (_img != null)
{
_img.Dispose();
}
_img = imgNew;

int zoom;

if (!int.TryParse(textBox2.Text, out zoom))
{
zoom = 100;
textBox2.Text = zoom.ToString();
}

pictureBox1.Width = imgNew.Width * zoom / 100;
pictureBox1.Height = imgNew.Height * zoom / 100;

_UpdatePixelRGB();
}

private void _ResetTimer(Timer timer)
{
if (timer.Enabled)
{
timer.Enabled = false;
}
timer.Enabled = true;
}

private void _UpdatePixelRGB()
{
StringBuilder sb = new StringBuilder(textBox3.Text.Length);

foreach (char ch in textBox3.Text)
{
if (Char.IsDigit(ch) || ch == ',')
{
sb.Append(ch);
}
}

string[] rgstrCoords = sb.ToString().Split(',');
int x, y;

if (rgstrCoords.Length != 2 ||
!int.TryParse(rgstrCoords[0], out x) ||
!int.TryParse(rgstrCoords[1], out y))
{
x = y = 0;
textBox3.Text = string.Format("{0},{1}", x.ToString(),
y.ToString());
}

Color color = ((Bitmap)_img).GetPixel(x, y);
label5.Text = string.Format("({3}, {4}) R: {0}, G: {1}, B:
{2}",
color.R.ToString(), color.G.ToString(), color.B.ToString(),
x.ToString(), y.ToString());
}

/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;

/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be
disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}

#region Windows Form Designer generated code

/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.panel1 = new System.Windows.Forms.Panel();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.label3 = new System.Windows.Forms.Label();
this.textBox3 = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.panel1.SuspendLayout();

((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 13);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(68, 13);
this.label1.TabIndex = 0;
this.label1.Text = "Line Interval:";
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(87, 10);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 1;
this.textBox1.Text = "1";
//
// panel1
//
this.panel1.Anchor =
((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top
| System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.panel1.AutoScroll = true;
this.panel1.Controls.Add(this.pictureBox1);
this.panel1.Location = new System.Drawing.Point(12, 63);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(370, 267);
this.panel1.TabIndex = 2;
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(87, 37);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(100, 20);
this.textBox2.TabIndex = 3;
this.textBox2.Text = "100";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(4, 40);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(77, 13);
this.label2.TabIndex = 4;
this.label2.Text = "Zoom Percent:";
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(100, 50);
this.pictureBox1.SizeMode =
System.Windows.Forms.PictureBoxSizeMode.StretchImage;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(218, 13);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(32, 13);
this.label3.TabIndex = 5;
this.label3.Text = "Pixel:";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(256, 10);
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(100, 20);
this.textBox3.TabIndex = 6;
this.textBox3.Text = "0,0";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(218, 37);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(33, 13);
this.label4.TabIndex = 7;
this.label4.Text = "RGB:";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(257, 37);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(35, 13);
this.label5.TabIndex = 8;
this.label5.Text = "label5";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(394, 342);
this.Controls.Add(this.label5);
this.Controls.Add(this.label4);
this.Controls.Add(this.textBox3);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.textBox2);
this.Controls.Add(this.panel1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.label1);
this.Name = "Form1";
this.Text = "Form1";
this.panel1.ResumeLayout(false);

((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();

}

#endregion

private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label5;
}
}
 
G

Geoffrey Summerhayes

I'm having trouble with what seems to be a simple task:

I'm trying to draw some vector images on a field of tiles.  The tiles
are always of integer sizes and can zoom over any reasonable range of
values.

The images themselves are vector images but they don't simply scale
up, as the tile gets bigger more detail is added.  (Yeah, I know I'll
have to handle printing a bit differently.)

The problem I'm having is with images composed entirely of vertical
and horizontal lines, black on a gray field.

The horizontal lines are fine, I get exactly what I want.  Vertical
lines aren't working too well, though.  A lone vertical line is good
enough--two pixels of dark gray instead of one of black but it looks
close enough I didn't even realize it was wrong until I got digging
into the case that's awful:

I have some stripes, some are black/background/background and others
are black/background.

With antialiasing on the ones with the black every three look fuzzy
and the closer ones just look like a blurry blob.

With it turned off I'm getting a very clear image, albeit very wrong:
The three-step one produces dark gray/dark gray/background and the
two-step one simply produces a box of dark gray.

Everything is being drawn on a bitmap that is then put in a
picturebox.  The picturebox is in a panel that fills the form other
than a few controls on the right for manipulating the view.

The behavior would make sense if somehow I was drawing between pixels
on the y axis but all the DrawLines are being fed integer coordinates.

My psychic powers must be off today, everything is blurry. Can't see
the code, the size of the bitmap, the size of the picturebox,
properties
of the picturebox, the comments in the code, in fact, even the code!

On a guess, try making the window 1 pixel wider, see if anything
changes.
 
J

Jeff Johnson

When you create the Pen you're using to draw your line are you passing a
value of -1 for the width? If not, do so.
 
V

vanderghast

You may get that kind of problem with some LCD monitors while being not in
the recommended resolution and, in those cases, that is a problem related to
hardware and your choice of resolution. As usual, your problem may be
something else, though.

Vanderghast, Access MVP
 
L

Loren Pechtel

Loren said:
[...]
The behavior would make sense if somehow I was drawing between pixels
on the y axis but all the DrawLines are being fed integer coordinates.

With no scaling/transformation whatsoever? And you are displaying the
images also without any scaling or transformation of any sort? How are
you determining what the exact pixel values are?

Your description sounds as though you're drawing on fractional
coordinates, in spite of your statement that you're not. But it's also
possible you're simply not gathering your data correctly and that the
pixels in the bitmap aren't what you think.

I agree it sounds exactly like fractional coordinates. How do you
draw on fractional coordinates with integer variables, though? There
is no float variable in any routine in the file that actually draws
anything.
 
L

Loren Pechtel

When you create the Pen you're using to draw your line are you passing a
value of -1 for the width? If not, do so.

Google kicked that one up already. Width = -1.
 
L

Loren Pechtel

You may get that kind of problem with some LCD monitors while being not in
the recommended resolution and, in those cases, that is a problem related to
hardware and your choice of resolution. As usual, your problem may be
something else, though.

Native 1280x1024.
 
L

Loren Pechtel

My psychic powers must be off today, everything is blurry. Can't see
the code, the size of the bitmap, the size of the picturebox,
properties
of the picturebox, the comments in the code, in fact, even the code!

I was hoping someone could point me in the right direction without
having to distill down an example. While I've been programming a long
time I'm new to C# so I figure I'm probably making an elementary
mistake.
On a guess, try making the window 1 pixel wider, see if anything
changes.

No effect.
 
P

Peter Duniho

Loren said:
I agree it sounds exactly like fractional coordinates. How do you
draw on fractional coordinates with integer variables, though? [...]

By drawing in an environment where coordinates given as integers are
scaling somehow.

I posted a code example that shows that in general, the problem you
describe does not occur. Either your own code does something different,
or you are simply not observing the results correctly (i.e. you think
there's a problem when there's not).

In the latter case, obviously there's nothing to fix. In the former
case, until you post a concise-but-complete code example that reliably
demonstrates the issue, there's no way to tell you how to fix your code.

Pete
 
J

Jeff Johnson

I'm having trouble with what seems to be a simple task:

I'm trying to draw some vector images on a field of tiles. The tiles
are always of integer sizes and can zoom over any reasonable range of
values.

The images themselves are vector images but they don't simply scale
up, as the tile gets bigger more detail is added. (Yeah, I know I'll
have to handle printing a bit differently.)

The problem I'm having is with images composed entirely of vertical
and horizontal lines, black on a gray field.

The horizontal lines are fine, I get exactly what I want. Vertical
lines aren't working too well, though. A lone vertical line is good
enough--two pixels of dark gray instead of one of black but it looks
close enough I didn't even realize it was wrong until I got digging
into the case that's awful:

I have some stripes, some are black/background/background and others
are black/background.

With antialiasing on the ones with the black every three look fuzzy
and the closer ones just look like a blurry blob.

With it turned off I'm getting a very clear image, albeit very wrong:
The three-step one produces dark gray/dark gray/background and the
two-step one simply produces a box of dark gray.

Everything is being drawn on a bitmap that is then put in a
picturebox. The picturebox is in a panel that fills the form other
than a few controls on the right for manipulating the view.

The behavior would make sense if somehow I was drawing between pixels
on the y axis but all the DrawLines are being fed integer coordinates.

I have to ask: are you absolutely positive you're actually getting
different-colored pixels, or does it just LOOK that way. In other words,
have you saved your bitmap to disk and then loaded it into a graphics
application and increased the zoom so that you can see the individual
pixels?
 
P

Peter Duniho

Jeff said:
I have to ask: are you absolutely positive you're actually getting
different-colored pixels, or does it just LOOK that way. In other words,
have you saved your bitmap to disk and then loaded it into a graphics
application and increased the zoom so that you can see the individual
pixels?

To elaborate on this point: the code example I posted last evening is
specifically designed to illustrate this point. It provides for scaling
the image during presentation, as well as inspecting RGB values for any
specific pixel.

With the example, you can see that scaling the image by (for example)
200%, you get the aliasing behavior described by the OP, but the pixel
values are still exactly as he wants (i.e. 0,0,0 RGB where a black line
is drawn, 128,128,128 on the gray background where it wasn't).

Pete
 
L

Loren Pechtel

To elaborate on this point: the code example I posted last evening is
specifically designed to illustrate this point. It provides for scaling
the image during presentation, as well as inspecting RGB values for any
specific pixel.

With the example, you can see that scaling the image by (for example)
200%, you get the aliasing behavior described by the OP, but the pixel
values are still exactly as he wants (i.e. 0,0,0 RGB where a black line
is drawn, 128,128,128 on the gray background where it wasn't).

There should be no scaling in my case--I want to add more detail as
the image gets bigger and thus I'm drawing it at the exact size I want
(or using scroll bars if the minimum acceptable size is bigger than
the window size.)
 
L

Loren Pechtel

I have to ask: are you absolutely positive you're actually getting
different-colored pixels, or does it just LOOK that way. In other words,
have you saved your bitmap to disk and then loaded it into a graphics
application and increased the zoom so that you can see the individual
pixels?

When stripes turn into a blob instead it's quite obvious. While I was
working on features other than stripes I didn't realize anything was
wrong, it was close enough.
 
L

Loren Pechtel

Loren said:
I agree it sounds exactly like fractional coordinates. How do you
draw on fractional coordinates with integer variables, though? [...]

By drawing in an environment where coordinates given as integers are
scaling somehow.

I posted a code example that shows that in general, the problem you
describe does not occur. Either your own code does something different,
or you are simply not observing the results correctly (i.e. you think
there's a problem when there's not).

In the latter case, obviously there's nothing to fix. In the former
case, until you post a concise-but-complete code example that reliably
demonstrates the issue, there's no way to tell you how to fix your code.

I managed to reproduce it very easily:

A form containing a panel that contains a picture box. Drawing:

Bitmap Image = new Bitmap(Box.ClientSize.Width
,Box.ClientSize.Height);
Pen Ink = new Pen(Color.Black, -1);
using (Graphics Paper = Graphics.FromImage(Image))
{
Paper.Clear(Color.LightGray);
for (int x = 2; x < Image.Width / 2; x += 2)
Paper.DrawLine(Ink, x, 0, x, Image.Height - 1);
for (int y = 2; y < Image.Height; y += 2)
Paper.DrawLine(Ink, Image.Width / 2, y, Image.Width -
1, y);
}
Box.Image = Image;

Obviously the panel in this case is useless but I simply copied the
layout from the main code--in the main code there are some controls
off to the side and I use the panel to make the picture box take up
the whole form.

This code *SHOULD* draw one half of the screen with vertical lines and
one half with horizontal. I'm getting a gray mass on the left and the
expected behavior on the right.
 
F

Family Tree Mike

Loren said:
Loren said:
I agree it sounds exactly like fractional coordinates. How do you
draw on fractional coordinates with integer variables, though? [...]
By drawing in an environment where coordinates given as integers are
scaling somehow.

I posted a code example that shows that in general, the problem you
describe does not occur. Either your own code does something different,
or you are simply not observing the results correctly (i.e. you think
there's a problem when there's not).

In the latter case, obviously there's nothing to fix. In the former
case, until you post a concise-but-complete code example that reliably
demonstrates the issue, there's no way to tell you how to fix your code.

I managed to reproduce it very easily:

A form containing a panel that contains a picture box. Drawing:

Bitmap Image = new Bitmap(Box.ClientSize.Width
,Box.ClientSize.Height);
Pen Ink = new Pen(Color.Black, -1);
using (Graphics Paper = Graphics.FromImage(Image))
{
Paper.Clear(Color.LightGray);
for (int x = 2; x < Image.Width / 2; x += 2)
Paper.DrawLine(Ink, x, 0, x, Image.Height - 1);
for (int y = 2; y < Image.Height; y += 2)
Paper.DrawLine(Ink, Image.Width / 2, y, Image.Width -
1, y);
}
Box.Image = Image;

Obviously the panel in this case is useless but I simply copied the
layout from the main code--in the main code there are some controls
off to the side and I use the panel to make the picture box take up
the whole form.

This code *SHOULD* draw one half of the screen with vertical lines and
one half with horizontal. I'm getting a gray mass on the left and the
expected behavior on the right.

For what it is worth, I see what you say it *SHOULD* draw when I execute
your routine.
 
L

Loren Pechtel

Loren said:
Loren Pechtel wrote:
I agree it sounds exactly like fractional coordinates. How do you
draw on fractional coordinates with integer variables, though? [...]
By drawing in an environment where coordinates given as integers are
scaling somehow.

I posted a code example that shows that in general, the problem you
describe does not occur. Either your own code does something different,
or you are simply not observing the results correctly (i.e. you think
there's a problem when there's not).

In the latter case, obviously there's nothing to fix. In the former
case, until you post a concise-but-complete code example that reliably
demonstrates the issue, there's no way to tell you how to fix your code.

I managed to reproduce it very easily:

A form containing a panel that contains a picture box. Drawing:

Bitmap Image = new Bitmap(Box.ClientSize.Width
,Box.ClientSize.Height);
Pen Ink = new Pen(Color.Black, -1);
using (Graphics Paper = Graphics.FromImage(Image))
{
Paper.Clear(Color.LightGray);
for (int x = 2; x < Image.Width / 2; x += 2)
Paper.DrawLine(Ink, x, 0, x, Image.Height - 1);
for (int y = 2; y < Image.Height; y += 2)
Paper.DrawLine(Ink, Image.Width / 2, y, Image.Width -
1, y);
}
Box.Image = Image;

Obviously the panel in this case is useless but I simply copied the
layout from the main code--in the main code there are some controls
off to the side and I use the panel to make the picture box take up
the whole form.

This code *SHOULD* draw one half of the screen with vertical lines and
one half with horizontal. I'm getting a gray mass on the left and the
expected behavior on the right.

For what it is worth, I see what you say it *SHOULD* draw when I execute
your routine.

What in the world is the difference then? How do I get it to draw
what you're seeing?
 
F

Family Tree Mike

Loren said:
What in the world is the difference then? How do I get it to draw
what you're seeing?

I suspect screen settings. When I set my display settings really
poorly, then I see what you observe. My normal settings are 1680x1050
32 bit color.
 
P

Peter Duniho

Loren said:
What in the world is the difference then? How do I get it to draw
what you're seeing?

You've yet to establish it's _not_ drawing what Mike is seeing.

Visual inspection isn't good enough. Just because you've drawn a bitmap
a certain way, that's no guarantee it will be presented on the screen
that way.

Pete
 
P

Peter Duniho

Loren said:
There should be no scaling in my case [...]

So you say. But until you post a concise-but-complete code example,
it's not possible to know for sure there's not. If I had a nickel for
every time someone asked a question, 100% certain that their description
of the situation was accurate, only to find it out wasn't...

And even if we establish there's no scaling in the _drawing_ of the
bitmap, there's still some possibility you've got some non-standard
system configuration that is meddling with the display on-screen.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top