Bitmap high memory usage - c#

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();
}
}

Related

C# windows form render isometric tile

I know so many questions have been already asked based on this topic and still i am unable to generate tiles in isometric view.
please check the below code and its out put
private void picIsometricBox_Paint(object sender, PaintEventArgs e)
{
try
{
int paddingTop = sdpaddingTop.Value * -80;
int paddingleft = sdpaddingLeft.Value * 40;
int rotateangle = sbRotate.Value * 5;
int noofCells = 9;
int cellsize = 60;
int x = 0;
for (int j = 1; j <= noofCells; j++)
{
for (int i = 1; i <= noofCells; i++)
{
var ep = (x % 2) == 0 ? Brushes.White : Brushes.Black;
int xv = (j * cellsize + paddingleft);
int yv = (i * cellsize) + paddingTop;
int xxv = (i * cellsize) + paddingleft;
int yyv = cellsize + paddingTop;
e.Graphics.RotateTransform(rotateangle);
e.Graphics.FillRectangle(ep, xv, yv, cellsize, cellsize);
e.Graphics.FillRectangle(ep, xxv, yyv, cellsize, cellsize);
e.Graphics.ResetTransform();
x++;
}
}
}
catch (Exception ex)
{
}
}
output is rendered in picture box:
I am not sure how to generate tiles like below

C# drawing houses that become 20% smaller than house on left

I want to draw a street with a panel but it wont work. I want to have it with a loop but i cant get it done. The wall and roof must become 20% smaller than the house on the left.
My code:
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 5; i++)
{
HuisTekenen();
}
}
private void HuisTekenen()
{
gebouw();
dak();
}
private void gebouw()
{
Graphics paper;
paper = panel1.CreateGraphics();
Pen pennetje = new Pen(Color.Green);
int b = 100;
int aantal = 0;
for (int i = 10; aantal <= 5; i += 120)
{
paper.DrawRectangle(pennetje, i, 100, b, 150);
aantal++;
i = i / 100 * 80;
b = b / 100 * 80;
}
}
private void dak()
{
Graphics paper;
paper = panel1.CreateGraphics();
Pen pennetje = new Pen(Color.Red);
int b = 100;
int aantal = 0;
for (int i = 10; aantal <= 5; i+=120)
{
paper.DrawLine(pennetje, i, 10 / 100 * 80, i, b);
paper.DrawLine(pennetje, i, 10 / 100 * 80, i + 100, b);
aantal++;
i = i / 100 * 80;
b = 100 / 100 * 80;
}
}
The result i want to get:
Result i get:
Can you help me?
Thanks!
Here's a way to get started with the box. Youll have to finish your homework on your own.
float size = 50;
float xpos = 0;
float ypos = 0;
for(int i=0;i<5;i++) //whatever you do, this must be what your loop looks like. anything else is going way off in the wrong direction
{
paper.DrawRectangle(pennetje, xpos, ypos, size, size);
xpos += size + 20;
size *= .8f;
}
First a remark: your for-statements are odd, usually the three parts will use the same variable. It can work this way, but doing it like this is certainly not recommended.
A different kind of issue will be this statement:
i = i / 100 * 80;
With variable i being an int, and / performing integer division, the first part i / 100 will produce 0 because that is the int value closest to the real/float result. And of course multiplying 0 by 80 will not do anything.
I suggest you now try again :-)

Setting more values to 1 in a array?

class spielfeld
{
int[,] spielfeldgitter = new int[16, 16];
public void spielfeldnullsetzen(/*PictureBox pictureBox1*/)
{
for (int i = 0; i < spielfeldgitter.GetLength(0); i++)
{
for (int j = 0; j < spielfeldgitter.GetLength(1); j++)
{
spielfeldgitter[i, j] = 0;
}
}
}
public void spielfeldol(PictureBox pictureBox1)
{
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics feld = Graphics.FromImage(bmp);
for (int i = 0; i < 16; i++)
{
feld.DrawLine(Pens.Black, 0, (320 / 16) * i, 320, (320 / 16) * i);
}
for (int i = 0; i < 16; i++)
{
feld.DrawLine(Pens.Black, (320 / 16) * i, 0, (320 / 16) * i, 320);
}
pictureBox1.Image = bmp;
}
public void mouseclick(int eX, int eY, PictureBox pictureBox1)
{
int cellw = (eX / 20);
int cellh = (eY / 20);
if (spielfeldgitter[cellw, cellh] != 1)
{
spielfeldgitter[cellw, cellh] = 1;
Graphics rectangle = Graphics.FromImage(pictureBox1.Image);
rectangle.FillRectangle(Brushes.Green, ((320 / 16) * cellw + 1), (320 / 16) * cellh + 1, (320 / 16) - 1, (320 / 16) - 1);
}
else
{
spielfeldgitter[cellw, cellh] = 0;
Graphics rectangle = Graphics.FromImage(pictureBox1.Image);
rectangle.FillRectangle(Brushes.White, ((320 / 16) * cellw + 1), (320 / 16) * cellh + 1, (320 / 16) - 1, (320 / 16) - 1);
}
}
}
SO its the snippet of the src with the logic things im stuck with.
private void Form1_Load(object sender, EventArgs e)
{
Feld.spielfeldnullsetzen(/*pictureBox1*/);
Feld.spielfeldol(pictureBox1);
}
private void pictureBox1_Click(object sender, EventArgs e)
{
Feld.spielfeldol(pictureBox1);
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
int eX = e.X;
int eY = e.Y;
Feld.spielfeldol(pictureBox1);
Feld.mouseclick(eX, eY, pictureBox1);
}
}
thats the form.cs
my problem is i am creating a 2d array 16,16 big, and set every value to 0. Breakpoint says the same thing when i check it.
then when i do breakpoint at mouseclick
the specific array value changes and the colour of the rectangle too. but i cant manage to make more than one field in another colour
maybe someone can help?
public partial class Form1 : Form
{
spielfeld Feld = new spielfeld();
public Form1()
{
InitializeComponent();
Load += Form1_Load;
//pictureBox1.Click += pictureBox1_Click;
pictureBox1.MouseClick += pictureBox1_MouseClick;
}
private void Form1_Load(object sender, EventArgs e)
{
Feld.spielfeldnullsetzen();
Feld.spielfeldol(pictureBox1);
}
//private void pictureBox1_Click(object sender, EventArgs e)
//{
// Feld.spielfeldol(pictureBox1);
//}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
int eX = e.X;
int eY = e.Y;
//Feld.spielfeldol(pictureBox1);
Feld.mouseclick(eX, eY, pictureBox1);
}
}
Together with this class
class spielfeld
{
int[,] spielfeldgitter = new int[16, 16];
public void spielfeldnullsetzen(/*PictureBox pictureBox1*/)
{
for (int i = 0; i < spielfeldgitter.GetLength(0); i++)
{
for (int j = 0; j < spielfeldgitter.GetLength(1); j++)
{
spielfeldgitter[i, j] = 0;
}
}
}
public void spielfeldol(PictureBox pictureBox1)
{
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphics feld = Graphics.FromImage(bmp);
for (int i = 0; i < 16; i++)
{
feld.DrawLine(Pens.Black, 0, (320 / 16) * i, 320, (320 / 16) * i);
}
for (int i = 0; i < 16; i++)
{
feld.DrawLine(Pens.Black, (320 / 16) * i, 0, (320 / 16) * i, 320);
}
pictureBox1.Image = bmp;
}
public void mouseclick(int eX, int eY, PictureBox pictureBox1)
{
int cellw = (eX / 20);
int cellh = (eY / 20);
if (spielfeldgitter[cellw, cellh] != 1)
{
spielfeldgitter[cellw, cellh] = 1;
Graphics rectangle = Graphics.FromImage(pictureBox1.Image);
rectangle.FillRectangle(Brushes.Green, ((320 / 16) * cellw + 1), (320 / 16) * cellh + 1, (320 / 16) - 1, (320 / 16) - 1);
}
else
{
spielfeldgitter[cellw, cellh] = 0;
Graphics rectangle = Graphics.FromImage(pictureBox1.Image);
rectangle.FillRectangle(Brushes.White, ((320 / 16) * cellw + 1), (320 / 16) * cellh + 1, (320 / 16) - 1, (320 / 16) - 1);
}
pictureBox1.Refresh();
}
}
Should get you started.
Changes to your original code:
pictureBox1.Refresh() in the board mouseclick method
changes in the form are put in comment
That's because spielfeldol clears all image and mouseclick draws only one rectangle in previously cleared image. So there are few options to fix this:
Remove spielfeldol method call from pictureBox1_MouseClick event handler and add some kind of button to call spielfeldol method for clearing image.
In spielfeldol method reuse pictureBox1.Image (do not create new Bitmap - it will be empty) like you do in 'mouseclick' method.

How to create a "sharp" gradient in Windows Forms?

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!

Open source C# code to present wave form?

Is there any open source C# code or library to present a graphical waveform given a byte array?
This is as open source as it gets:
public static void DrawNormalizedAudio(ref float[] data, PictureBox pb,
Color color)
{
Bitmap bmp;
if (pb.Image == null)
{
bmp = new Bitmap(pb.Width, pb.Height);
}
else
{
bmp = (Bitmap)pb.Image;
}
int BORDER_WIDTH = 5;
int width = bmp.Width - (2 * BORDER_WIDTH);
int height = bmp.Height - (2 * BORDER_WIDTH);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.Black);
Pen pen = new Pen(color);
int size = data.Length;
for (int iPixel = 0; iPixel < width; iPixel++)
{
// determine start and end points within WAV
int start = (int)((float)iPixel * ((float)size / (float)width));
int end = (int)((float)(iPixel + 1) * ((float)size / (float)width));
float min = float.MaxValue;
float max = float.MinValue;
for (int i = start; i < end; i++)
{
float val = data[i];
min = val < min ? val : min;
max = val > max ? val : max;
}
int yMax = BORDER_WIDTH + height - (int)((max + 1) * .5 * height);
int yMin = BORDER_WIDTH + height - (int)((min + 1) * .5 * height);
g.DrawLine(pen, iPixel + BORDER_WIDTH, yMax,
iPixel + BORDER_WIDTH, yMin);
}
}
pb.Image = bmp;
}
This function will produce something like this:
This takes an array of samples in floating-point format (where all sample values range from -1 to +1). If your original data is actually in the form of a byte[] array, you'll have to do a little bit of work to convert it to float[]. Let me know if you need that, too.
Update: since the question technically asked for something to render a byte array, here are a couple of helper methods:
public float[] FloatArrayFromStream(System.IO.MemoryStream stream)
{
return FloatArrayFromByteArray(stream.GetBuffer());
}
public float[] FloatArrayFromByteArray(byte[] input)
{
float[] output = new float[input.Length / 4];
for (int i = 0; i < output.Length; i++)
{
output[i] = BitConverter.ToSingle(input, i * 4);
}
return output;
}
Update 2: I forgot there's a better way to do this:
public float[] FloatArrayFromByteArray(byte[] input)
{
float[] output = new float[input.Length / 4];
Buffer.BlockCopy(input, 0, output, 0, input.Length);
return output;
}
I'm just so in love with for loops, I guess.
I modified MusiGenesis's solution a little bit.
This gave me a much better result, especially with house music :)
public static Bitmap DrawNormalizedAudio(List<float> data, Color foreColor, Color backColor, Size imageSize)
{
Bitmap bmp = new Bitmap(imageSize.Width, imageSize.Height);
int BORDER_WIDTH = 0;
float width = bmp.Width - (2 * BORDER_WIDTH);
float height = bmp.Height - (2 * BORDER_WIDTH);
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(backColor);
Pen pen = new Pen(foreColor);
float size = data.Count;
for (float iPixel = 0; iPixel < width; iPixel += 1)
{
// determine start and end points within WAV
int start = (int)(iPixel * (size / width));
int end = (int)((iPixel + 1) * (size / width));
if (end > data.Count)
end = data.Count;
float posAvg, negAvg;
averages(data, start, end, out posAvg, out negAvg);
float yMax = BORDER_WIDTH + height - ((posAvg + 1) * .5f * height);
float yMin = BORDER_WIDTH + height - ((negAvg + 1) * .5f * height);
g.DrawLine(pen, iPixel + BORDER_WIDTH, yMax, iPixel + BORDER_WIDTH, yMin);
}
}
return bmp;
}
private static void averages(List<float> data, int startIndex, int endIndex, out float posAvg, out float negAvg)
{
posAvg = 0.0f;
negAvg = 0.0f;
int posCount = 0, negCount = 0;
for (int i = startIndex; i < endIndex; i++)
{
if (data[i] > 0)
{
posCount++;
posAvg += data[i];
}
else
{
negCount++;
negAvg += data[i];
}
}
posAvg /= posCount;
negAvg /= negCount;
}
with adapted code from robby and using Graphics.Fill/DrawClosedCurve with antialiasing, I get a pretty good looking result.
here's the code:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Soundfingerprinting.Audio.Services
{
public static class AudioVisualizationService
{
public class WaveVisualizationConfiguration
{
public Nullable<Color> AreaColor { get; set; }
public Nullable<Color> EdgeColor { get; set; }
public int EdgeSize { get; set; }
public Nullable<Rectangle> Bounds { get; set; }
public double Overlap { get; set; }
public int Step { get; set; }
}
public static void DrawWave(float[] data, Bitmap bitmap, WaveVisualizationConfiguration config = null)
{
Color areaColor = Color.FromArgb(0x7F87CEFA);// Color.LightSkyBlue; semi transparent
Color edgeColor = Color.DarkSlateBlue;
int edgeSize = 2;
int step = 2;
double overlap = 0.10f; // would better use a windowing function
Rectangle bounds = Rectangle.FromLTRB(0, 0, bitmap.Width, bitmap.Height);
if (config != null)
{
edgeSize = config.EdgeSize;
if (config.AreaColor.HasValue)
areaColor = config.AreaColor.GetValueOrDefault();
if (config.EdgeColor.HasValue)
edgeColor = config.EdgeColor.GetValueOrDefault();
if (config.Bounds.HasValue)
bounds = config.Bounds.GetValueOrDefault();
step = Math.Max(1, config.Step);
overlap = config.Overlap;
}
float width = bounds.Width;
float height = bounds.Height;
using (Graphics g = Graphics.FromImage(bitmap))
{
Pen edgePen = new Pen(edgeColor);
edgePen.LineJoin = LineJoin.Round;
edgePen.Width = edgeSize;
Brush areaBrush = new SolidBrush(areaColor);
float size = data.Length;
PointF[] topCurve = new PointF[(int)width / step];
PointF[] bottomCurve = new PointF[(int)width / step];
int idx = 0;
for (float iPixel = 0; iPixel < width; iPixel += step)
{
// determine start and end points within WAV
int start = (int)(iPixel * (size / width));
int end = (int)((iPixel + step) * (size / width));
int window = end - start;
start -= (int)(overlap * window);
end += (int)(overlap * window);
if (start < 0)
start = 0;
if (end > data.Length)
end = data.Length;
float posAvg, negAvg;
averages(data, start, end, out posAvg, out negAvg);
float yMax = height - ((posAvg + 1) * .5f * height);
float yMin = height - ((negAvg + 1) * .5f * height);
float xPos = iPixel + bounds.Left;
if (idx >= topCurve.Length)
idx = topCurve.Length - 1;
topCurve[idx] = new PointF(xPos, yMax);
bottomCurve[bottomCurve.Length - idx - 1] = new PointF(xPos, yMin);
idx++;
}
PointF[] curve = new PointF[topCurve.Length * 2];
Array.Copy(topCurve, curve, topCurve.Length);
Array.Copy(bottomCurve, 0, curve, topCurve.Length, bottomCurve.Length);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillClosedCurve(areaBrush, curve, FillMode.Winding, 0.15f);
if (edgeSize > 0)
g.DrawClosedCurve(edgePen, curve, 0.15f, FillMode.Winding);
}
}
private static void averages(float[] data, int startIndex, int endIndex, out float posAvg, out float negAvg)
{
posAvg = 0.0f;
negAvg = 0.0f;
int posCount = 0, negCount = 0;
for (int i = startIndex; i < endIndex; i++)
{
if (data[i] > 0)
{
posCount++;
posAvg += data[i];
}
else
{
negCount++;
negAvg += data[i];
}
}
if (posCount > 0)
posAvg /= posCount;
if (negCount > 0)
negAvg /= negCount;
}
}
}
In NAudio, there is code to draw audio waveforms in both WinForms and WPF. Have a look at the demo projects for examples of how to use it.
I've been a fan of ZedGraph for many years and have used it to display all kinds of data in various projects.
The following sample code graphs an array of doubles varying between -1 and 1:
void DisplayWaveGraph(ZedGraphControl graphControl, double[] waveData)
{
var pane = graphControl.GraphPane;
pane.Chart.Border.IsVisible = false;
pane.Chart.Fill.IsVisible = false;
pane.Fill.Color = Color.Black;
pane.Margin.All = 0;
pane.Title.IsVisible = false;
pane.XAxis.IsVisible = false;
pane.XAxis.Scale.Max = waveData.Length - 1;
pane.XAxis.Scale.Min = 0;
pane.YAxis.IsVisible = false;
pane.YAxis.Scale.Max = 1;
pane.YAxis.Scale.Min = -1;
var timeData = Enumerable.Range(0, waveData.Length)
.Select(i => (double) i)
.ToArray();
pane.AddCurve(null, timeData, waveData, Color.Lime, SymbolType.None);
graphControl.AxisChange();
}
The above sample mimics the style of an audio editor by suppressing the axes and changing the colors to produce the following:

Categories

Resources