I need to fill a rectangle with a black to white (transparent) gradient. However, I could only find a GradientBrush class and all examples I found showed smooth transition and I want sharp bars. That's what I need:
You need to average the colors between your start color and your end color. Here is a routine that does all that, using an averaging formula found here: Generate Color Gradient in C#
private void PaintGradientBars(Graphics g, Rectangle r,
Color startColor, Color endColor, int numBars) {
int rMin = startColor.R;
int gMin = startColor.G;
int bMin = startColor.B;
int rMax = endColor.R;
int gMax = endColor.G;
int bMax = endColor.B;
int left = 0;
for (int i = 0; i < numBars; i++) {
int rAvg = rMin + (int)((rMax - rMin) * i / numBars);
int gAvg = gMin + (int)((gMax - gMin) * i / numBars);
int bAvg = bMin + (int)((bMax - bMin) * i / numBars);
Color useColor = Color.FromArgb(rAvg, gAvg, bAvg);
int width = (r.Width - left) / (numBars - i);
using (SolidBrush br = new SolidBrush(useColor)) {
g.FillRectangle(br, new Rectangle(left, 0, width, r.Height));
}
left += width;
}
}
Then you make a simple call:
private void panel1_Paint(object sender, PaintEventArgs e) {
PaintGradientBars(e.Graphics, panel1.ClientRectangle,
Color.Blue, Color.Green, 5);
}
Resulting in:
in this code i use picturebox, play with 'k' and 'i'
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
int k = 20;
Color mycolor = new Color();
for (int i = 0; i < 10; i++)
{
mycolor = Color.FromArgb(i * k, i * k, i * k);
SolidBrush mybrash = new SolidBrush(mycolor);
e.Graphics.FillRectangle((Brush)mybrash, 0 + i * k, 0, k, k);
}
}
Good luck!
Related
I'm trying to create some simple graphics ("plasma" effect) with C# and Winforms.
I have two classes in my code, (main)Form1 and Plasmadraw.
In Plasmadraw I have following setup:
class Plasmadraw
{
int y;
int x;
double i;
double pii = 3.1415;
public void Draw(Graphics gfx, int addition)
{
for (int i = 0; i < 23040; i++)
{
x = x + 10;
if (x == 1920)
{
x = 0;
y = y + 10;
}
if (y == 1200)
{
y = 0;
}
double v = Math.Sin((x * 0.5) + i * 0.001 * addition);
double c = v * pii;
double d = c + (2 * pii / 3);
double f = c + (6 * pii / 3);
double r = 255 * Math.Abs(Math.Sin(c));
double g = 255 * Math.Abs(Math.Sin(d));
double b = 255 * Math.Abs(Math.Sin(f));
int r1 = (int)r;
int g1 = (int)g;
int b1 = (int)b;
Color e = Color.FromArgb(r1, g1, b1);
SolidBrush brush = new SolidBrush(e);
Rectangle rect = new Rectangle(x, y, 10, 10);
gfx.FillRectangle(brush, rect);
}
}
}
and then in Form1 I have Paint event:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Plasmadraw plasmaeffect = new Plasmadraw();
for (int a = 0; a < 30; a++)
{
plasmaeffect.Draw(e.Graphics, a);
Invalidate();
}
Is this somehow completely wrong way to build the logic ? I've managed to create some graphics effects (moving sprites etc.) by creating a list and then running through the list with Foreach in Paint event (& Invalidate()). However, I'd like to learn some new way to do things, rather than copying the old way.
I have an issue with my WinForms project. I need to display image of created maze and i use bitmap. But empty bitmap(9990, 9990) takes 400MB+. Is there way to decrease this memory consumption or i need to change bitmap to anything else?
Bitmap bm = new Bitmap(9990, 9990);
Thank you for your help.
The cell and wall have one size 10x10 px.
https://i.stack.imgur.com/yj9CA.png
I decreased the memory usage by using a custom PixelFormat;
It reduced memory consumption by 2-4 times.
var format = System.Drawing.Imaging.PixelFormat.Format16bppRgb565;
inBm = new Bitmap(
CellWid * (maze.finish.X + 2),
CellHgt * (maze.finish.Y + 2), format);
Is there a way to decrease memory consumption? As long as you do not need the whole maze rendered at once there is. You use 10*10*4 = 400B to store information about one cell. Chances are, you only need to know if the cell is a wall or not. That is 1 bit. You can reduce 400MB to 125kB to store information about the whole maze. And render only the part you actually need. Here is some code to play with, this draws 999x999 cells "maze" you can move by mouse
BitArray maze = null;
int mazeWidth = 999;
int mazeHeight = 999;
int xPos = 0;
int yPos = 0;
int cellSize = 10;
private void Form1_Load(object sender, EventArgs e)
{
maze = new BitArray(mazeWidth * mazeHeight);
Random rnd = new Random();
for (int i = 0; i < maze.Length; ++i)
{
maze[i] = rnd.Next(4) == 0;
}
xPos = -Width / 2;
yPos = -Height / 2;
DoubleBuffered = true;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
for (int y = Math.Max(0, yPos / cellSize); y < mazeHeight; ++y)
{
int yDraw = y * cellSize - yPos;
if (yDraw > Height) { return; }
for (int x = Math.Max(0, xPos / cellSize); x < mazeWidth; ++x)
{
if (maze[x + y * mazeWidth])
{
int xDraw = x * cellSize - xPos;
if (xDraw > Width) { break; }
e.Graphics.FillRectangle(
Brushes.Black,
xDraw,
yDraw,
cellSize,
cellSize
);
}
}
}
}
public static int Clamp(int value, int min, int max)
{
if (value < min) { return min; }
if (value > max) { return max; }
return value;
}
int fromX;
int fromY;
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
fromX = e.X;
fromY = e.Y;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
int w2 = Width / 2;
int h2 = Height / 2;
xPos = Clamp(xPos + fromX - e.X, -w2, mazeWidth * cellSize - w2);
yPos = Clamp(yPos + fromY - e.Y, -h2, mazeHeight * cellSize - h2);
fromX = e.X;
fromY = e.Y;
Invalidate();
}
}
I am using the below code to draw the circles(reading centres from a csv file) with increasing radius. The increase in radius is 5 units per circle.
namespace MATLAB_file
{
public partial class Form1 : Form
{
string[] read;
float th;
int c = 0;
int r;
public List<PointF> circleCoordinates = new List<PointF>();
int rl;
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
Pen linePen = new Pen(System.Drawing.Color.CornflowerBlue);
Graphics grphx = this.CreateGraphics();
grphx.Clear(this.BackColor);
foreach (PointF point in this.circleCoordinates)
{
Pen redPen1 = new Pen(Color.Red, 100);
e.Graphics.DrawArc(Pens.Red, point.X, point.Y, 1, 1, 0, 120F);
}
linePen.Dispose();
base.OnPaint(e);
}
private void Form1_Load(object sender, EventArgs e)
{
double xx, yy;
int i;
int n = 0;
float[] centre1 = new float[1000];
System.IO.StreamReader sr;
sr = new System.IO.StreamReader("centers.txt", true);
char[] seperators = { ',' };
string data = sr.ReadLine();
read = data.Split(new Char[] { ',' });
rl = read.Length;
int a1 = rl / 2;
for (c = 0; c < rl; c++)
{
centre1[c] = float.Parse(read[c]);
}
while (r < 200)
{
for (i = 0; i < a1; i++)
{
while (th < 360)
{
xx = r * Math.Cos(th) + centre1[2 * i] + 100;
xx1 = (float)xx;
yy = r * Math.Sin(th) + centre1[2 * i + 1] + 100;
yy1 = (float)yy;
this.circleCoordinates.Add(new PointF(xx1, yy1));
this.Invalidate();
th = th + .360F;
}
th = 0;
}
r = r + 5;
}
}
}
}
The above code is displaying all the circles but I do not want all circles to be displayed on canvas, rather only one circle should show with gradual increase in radius
Please suggest how to delete the previous drawn circle on drawing new one. Is there any other way to do it, if my later use includes removal of certain section of circles based on "th" values?
If you move the Clear call within the foreach you will see only the last drawn circle, though this could be achieved with drawing only the Last circleCoordinates too.
foreach (PointF point in this.circleCoordinates)
{
grphx.Clear(this.BackColor);
Pen redPen1 = new Pen(Color.Red, 100);
e.Graphics.DrawArc(Pens.Red, point.X, point.Y, 1, 1, 0, 120F);
}
An alternative interpretation:
You want animation, which generates the onPaint events (by timer ticks or on user input) and you increase a counter to select the circle to draw.
For that, you will need a new member in your program, like int Index; and you could select the circle based on this, using something the following code snippet (assuming you always have at least 1 circleCoordinates, animation restarts after it finished):
PointF point = this.circleCoordinates[Index % circleCoordinates.Length];
or (last circle remain on the screen after the animation)
PointF point = this.circleCoordinates[Math.Min(Index, circleCoordinates.Length - 1)];
I want create a control that draws a table in panel . My code is:
public class PanelZ : System.Windows.Forms.Panel
{
public static void Draw()
{
Panel p = new Panel();
p.Width = 200;
p.Height = 200;
Graphics g = p.CreateGraphics();
Pen mypen = new Pen(Brushes.Black, 1);
Font myfont = new Font("tahoma", 10);
int lines = 9;
float x = 0;
float y = 0;
float xSpace = p.Width / lines;
float yspace = p.Height / lines;
for (int i = 0; i < lines + 1; i++)
{
g.DrawLine(mypen, x, y, x, p.Height);
x += xSpace;
}
x = 0f;
for (int i = 0; i < lines + 1; i++)
{
g.DrawLine(mypen, x, y, p.Width, y);
y += yspace;
}
}
..but it dosen't draw a table; so what should I do?
This will work. But the numbers ought to be properties, as should the pen and then some.. Also: Properties ought to start with an uppercase letter.
public class PanelZ : System.Windows.Forms.Panel
{
public PanelZ() // a constructor
{
Width = 200;
Height = 200;
DoubleBuffered = true;
lines = 9;
}
public int lines { get; set; } // a property
protected override void OnPaint(PaintEventArgs e) // the paint event
{
base.OnPaint(e);
Graphics g = e.Graphics;
Pen mypen = new Pen(Brushes.Black, 1);
Font myfont = new Font("tahoma", 10);
float x = 0;
float y = 0;
float xSpace = Width / lines;
float yspace = Height / lines;
for (int i = 0; i < lines + 1; i++)
{
g.DrawLine(mypen, x, y, x, Height);
x += xSpace;
}
for (int i = 0; i < lines + 1; i++)
{
g.DrawLine(mypen, 0, y, Width, y);
y += yspace;
}
}
}
At work in VS:
Note that this only colors pixels. There is no useful grid there, just pixels with color.. So, if you actually want to use the Font you define you will have to calculate the coodordinates and the bounding boxes.
I'm working with VS C# 2010 and I'm creating a WPF application. I have to represent a matrix with different values, and to make them a bit easier to understand I'd like to use a ColorMap bar. I did create once a project in Windows Forms (it has System.Drawings but WPF doesn't). Is there any way of creating it or adapting it by creating my own "library"?
Here is an example of what I'd like to do:
But furthermore I'd like to set the ranges on the bar as well. If it helps, here is the code using ColorMap in Windows Forms (it does not work in WPF):
namespace Example1_8
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.ResizeRedraw, true);
this.BackColor = Color.White;
this.Width = 340;
this.Height = 340;
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
int width = 30;
int height = 128;
int y = 0;
// Create opaque color maps with alpha = 255:
ColorMap cm = new ColorMap();
DrawColorBar(g, 10 + 4 * 40, y, width, height, cm, "Jet");
}
private void DrawColorBar(Graphics g, int x, int y, int width, int height,
ColorMap map, string str)
{
int[,] cmap = new int[64, 4];
switch (str)
{
case "Jet":
cmap = map.Jet();
break;
}
int ymin = 0;
int ymax = 32;
int dy = height / (ymax - ymin);
int m = 64;
for (int i = 0; i < 32; i++)
{
int colorIndex = (int)((i - ymin) * m / (ymax - ymin));
SolidBrush aBrush = new SolidBrush(Color.FromArgb(
cmap[colorIndex, 0], cmap[colorIndex, 1],
cmap[colorIndex, 2], cmap[colorIndex, 3]));
g.FillRectangle(aBrush, x, y + i * dy, width, dy);
}
}
}
}
You need to add the reference to your project. Right click on Refrences => Add Reference.... In Assemblies type System.Drawing and include that to your project. Then just add needed namespaces. ColorMap class is in System.Drawing.Imaging.