how to rotate image through put in angle value in textbox using graphics in asp.net c#?
I take a textbox and image and button.I want to give value in angle in textbox and image will be rotate according to given value.so,i can only doing a simple button_click event ,but it is not proper for textbox.so,give simple code ...
protected void Button2_Click(object sender, EventArgs e)
{
string path = Server.MapPath(Image1.ImageUrl);
////create an image object from the image in that path
System.Drawing.Image img = System.Drawing.Image.FromFile(path);
////rotate the image
img.RotateFlip(RotateFlipType.Rotate90FlipXY);
////save the image out to the file
img.Save(path);
////release image file
img.Dispose();
}
Following code save the rotated image into newfile.png in current project folder path
Try This:
protected void Button2_Click1(object sender, EventArgs e)
{
string path = Server.MapPath(Image1.ImageUrl);
string newpath =Server.MapPath(#"~/filename.png");
////create an image object from the image in that path
System.Drawing.Bitmap img =new System.Drawing.Bitmap(path);
Bitmap map = RotateBitmap(COnvert.ToSingle(textBox1.Text), img);
map.Save(newpath);
////release image file
img.Dispose();
}
public Bitmap RotateBitmap(float Angle, Bitmap bm_in)
{
try
{
float wid = bm_in.Width;
float hgt = bm_in.Height;
Point[] corners = { new Point(0, 0), new Point(int.Parse(wid.ToString()), 0), new Point(0, int.Parse(hgt.ToString())), new Point(int.Parse(wid.ToString()), int.Parse(hgt.ToString())) };
int cx = int.Parse(wid.ToString()) / 2;
int cy = int.Parse(hgt.ToString()) / 2;
long i;
for (i = 0; i <= 3; i++)
{
corners[i].X -= Convert.ToInt32(cx.ToString());
corners[i].Y -= Convert.ToInt32(cy.ToString());
}
float theta = (float)(Angle * Math.PI / 180.0);
float sin_theta = (float)Math.Sin(theta);
float cos_theta = (float)Math.Cos(theta);
float X;
float Y;
for (i = 0; i <= 3; i++)
{
X = corners[i].X;
Y = corners[i].Y;
corners[i].X = (int)(X * cos_theta + Y * sin_theta);
corners[i].Y = (int)(-X * sin_theta + Y * cos_theta);
}
float xmin = corners[0].X;
float ymin = corners[0].Y;
for (i = 1; i <= 3; i++)
{
if (xmin > corners[i].X)
xmin = corners[i].X;
if (ymin > corners[i].Y)
ymin = corners[i].Y;
}
for (i = 0; i <= 3; i++)
{
corners[i].X -= int.Parse(xmin.ToString());
corners[i].Y -= int.Parse(ymin.ToString());
}
Bitmap bm_out = new Bitmap((int)(-2 * xmin), (int)(-2 * ymin));
Graphics gr_out = Graphics.FromImage(bm_out);
// ERROR: Not supported in C#: ReDimStatement
Point[] temp = new Point[3];
if (corners != null)
{
Array.Copy(corners, temp, Math.Min(corners.Length, temp.Length));
}
corners = temp;
gr_out.DrawImage(bm_in, corners);
return bm_out;
}
catch (Exception ex)
{
string s = ex.Message;
return bm_in;
}
}
Related
I want to remove grayness from the picture in pictureBox1, as in the first photo below. The second photo shows the formulas that can be used to do this. I wrote an approximate code, but the compiler throws an error, that variables red1, green1 and blue1 cannot be written to Color newPixel due to type incompatibility. Please help me fixing my code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void pictureBox1_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Title = "Відкрити зображення";
dlg.Filter = "jpg files (*.jpg)|*.jpg|All filles (*.*)|*.*";
if (dlg.ShowDialog() == DialogResult.OK)
{
pictureBox1.Image = new Bitmap(dlg.OpenFile());
pictureBox1.Height = pictureBox1.Image.Height;
pictureBox1.Width = pictureBox1.Image.Width;
}
dlg.Dispose();
label1.Visible = false;
}
private void button1_Click(object sender, EventArgs e)
{
Bitmap input = new Bitmap(pictureBox1.Image);
Bitmap output = new Bitmap(input.Width, input.Height);
int[] red = new int[256];
int[] green = new int[256];
int[] blue = new int[256];
for (int x = 0; x < input.Width; x++)
{
for (int y = 0; y < input.Height; y++)
{
Color pixel = ((Bitmap)pictureBox1.Image).GetPixel(x, y);
red[pixel.R]++;
green[pixel.G]++;
blue[pixel.B]++;
double Ri = red.Average();
double Gi = green.Average();
double Bi = blue.Average();
double Avg = (Ri+Bi+Gi)/3;
double red1 = red (Avg/Ri);
double green1 = green(Avg / Gi);
double blue1 = blue(Avg / Bi);
Color newPixel = ((Color)red1| (Color)green1 | (Color)blue1);
output.SetPixel(x, y, Color.((int)newPixel));
}
}
pictureBox2.Image = output;
}
}
}
You should use Color.FromArgb() to create the new color. However, this method expects 3 int as input and not double, so you need to convert your doubles to integers.
Simple type cast - (int)someDouble
This solution will simply remove any decimals from the double value (1.9 => 1):
double red1 = 123.45;
double green1 = 12.345;
double blue1 = 234.56;
Color newPixel = Color.FromArgb((int)red1, (int)green1, (int)blue1);
// R=123, G=12, B=234
Math.Round(someDouble)
This solution will round the double value to the nearest integer (1.5 => 2 and 1.49 => 1):
int red1 = (int)Math.Round(123.45);
int green1 = (int)Math.Round(12.345);
int blue1 = (int)Math.Round(234.56);
Color newPixel = Color.FromArgb(red1, green1, blue1);
// R=123, G=12, B=235
You misinterpreted the formula. N in the formula specifies the total number of the pixels in the image. The overlined letters means the average channel value over the image. These variable should be the type of double. I called them redAverage, greenAverage and blueAverage, respectively. And as you
already know, Avg is the average of these variables. Finally, R', G', B' are the new channel values calculated using the old values.
using System.Drawing.Imaging;
namespace Convert2Gray
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private Bitmap m_inputImage;
private Bitmap m_outputImage;
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
m_inputImage = (Bitmap)Image.FromFile("sample.png");
m_outputImage = new Bitmap(m_inputImage.Width, m_inputImage.Height, m_inputImage.PixelFormat);
pictureBox1.Image = m_inputImage;
pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
DoConversion();
pictureBox2.Image = m_outputImage;
pictureBox2.SizeMode = PictureBoxSizeMode.AutoSize;
}
private unsafe void DoConversion()
{
BitmapData inputBitmapData = m_inputImage.LockBits(new Rectangle(0, 0, m_inputImage.Width, m_inputImage.Height), ImageLockMode.ReadWrite, m_inputImage.PixelFormat);
BitmapData outputBitmapData = m_outputImage.LockBits(new Rectangle(0, 0, m_outputImage.Width, m_outputImage.Height), ImageLockMode.ReadWrite, m_outputImage.PixelFormat);
byte* inputScan0 = (byte*)inputBitmapData.Scan0;
byte* outputScan0 = (byte*)outputBitmapData.Scan0;
int inputStride = inputBitmapData.Stride;
int outputStride = outputBitmapData.Stride;
int bytesPerPixel = Image.GetPixelFormatSize(m_inputImage.PixelFormat) / 8;
double redAverage = 0.0;
double greenAverage = 0.0;
double blueAverage = 0.0;
double average = 0.0;
int pixelCount = m_inputImage.Width * m_inputImage.Height;
for (int y = 0; y < m_inputImage.Height; y++)
{
byte* inputCurrentRow = inputScan0 + y * inputStride;
byte* outputCurrentRow = outputScan0 + y * outputStride;
for (int x = 0; x < m_inputImage.Width; x++)
{
ColorBgr* inputColor = (ColorBgr*)(inputCurrentRow + x * bytesPerPixel);
redAverage += inputColor->R;
greenAverage += inputColor->G;
blueAverage += inputColor->B;
}
}
redAverage /= pixelCount;
greenAverage /= pixelCount;
blueAverage /= pixelCount;
average = (redAverage + greenAverage + blueAverage) / 3;
for (int y = 0; y < m_inputImage.Height; y++)
{
byte* inputCurrentRow = inputScan0 + y * inputStride;
byte* outputCurrentRow = outputScan0 + y * outputStride;
for (int x = 0; x < m_inputImage.Width; x++)
{
ColorBgr* inputColor = (ColorBgr*)(inputCurrentRow + x * bytesPerPixel);
ColorBgr* outputColor = (ColorBgr*)(outputCurrentRow + x * bytesPerPixel);
outputColor->R = (byte)(inputColor->R * average / redAverage);
outputColor->G = (byte)(inputColor->G * average / greenAverage);
outputColor->B = (byte)(inputColor->B * average / blueAverage);
}
}
m_inputImage.UnlockBits(inputBitmapData);
m_outputImage.UnlockBits(outputBitmapData);
}
private struct ColorBgr : IEquatable<ColorBgr>
{
public byte B;
public byte G;
public byte R;
public ColorBgr(byte b, byte g, byte r)
{
B = b;
G = g;
R = r;
}
public static bool operator ==(ColorBgr left, ColorBgr right)
{
return left.Equals(right);
}
public static bool operator !=(ColorBgr left, ColorBgr right)
{
return !(left == right);
}
public bool Equals(ColorBgr other)
{
return this.B == other.B && this.G == other.G && this.R == other.R;
}
public override bool Equals(object? obj)
{
if (obj is ColorBgr)
return Equals((ColorBgr)obj);
return false;
}
public override int GetHashCode()
{
return new byte[] { B, G, R }.GetHashCode();
}
}
}
}
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 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 am trying to use the combination of the timer class and a codeproject to rotate an image smoothly in a picture box control (Visual Studio 2010 C#). The problem I am having is either the picture doesn't rotate or I get a threading exception. Something about "the object is in use elsewhere". Here's the main parts of the code, I would greatly appreciate any help you can provide. Thank you.
private void Form1_Load(object sender, EventArgs e)
{
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
timer.Start();
}
private void timer_Elapsed(object sender, EventArgs e)
{
//Graphics graphic = Graphics.FromImage(pictureBox1.Image);
//graphic.RotateTransform(45);
this.Invoke(new MethodInvoker(delegate { RotateImage(pictureBox1.Image, 10); }));
}
public static Bitmap RotateImage(Image image, float angle)
{
// center of the image
float rotateAtX = image.Width / 2;
float rotateAtY = image.Height / 2;
bool bNoClip = false;
return RotateImage(image, rotateAtX, rotateAtY, angle, bNoClip);
}
public static Bitmap RotateImage(Image image, float angle, bool bNoClip)
{
// center of the image
float rotateAtX = image.Width / 2;
float rotateAtY = image.Height / 2;
return RotateImage(image, rotateAtX, rotateAtY, angle, bNoClip);
}
public static Bitmap RotateImage(Image image, float rotateAtX, float rotateAtY, float angle, bool bNoClip)
{
int W, H, X, Y;
if (bNoClip)
{
double dW = (double)image.Width;
double dH = (double)image.Height;
double degrees = Math.Abs(angle);
if (degrees <= 90)
{
double radians = 0.0174532925 * degrees;
double dSin = Math.Sin(radians);
double dCos = Math.Cos(radians);
W = (int)(dH * dSin + dW * dCos);
H = (int)(dW * dSin + dH * dCos);
X = (W - image.Width) / 2;
Y = (H - image.Height) / 2;
}
else
{
degrees -= 90;
double radians = 0.0174532925 * degrees;
double dSin = Math.Sin(radians);
double dCos = Math.Cos(radians);
W = (int)(dW * dSin + dH * dCos);
H = (int)(dH * dSin + dW * dCos);
X = (W - image.Width) / 2;
Y = (H - image.Height) / 2;
}
}
else
{
W = image.Width;
H = image.Height;
X = 0;
Y = 0;
}
//create a new empty bitmap to hold rotated image
Bitmap bmpRet = new Bitmap(W, H);
bmpRet.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//make a graphics object from the empty bitmap
Graphics g = Graphics.FromImage(bmpRet);
//Put the rotation point in the "center" of the image
g.TranslateTransform(rotateAtX + X, rotateAtY + Y);
//rotate the image
g.RotateTransform(angle);
//move the image back
g.TranslateTransform(-rotateAtX - X, -rotateAtY - Y);
//draw passed in image onto graphics object
g.DrawImage(image, new PointF(0 + X, 0 + Y));
return bmpRet;
}
I think you should invalidate and paint in you OnPaint or OnDraw event
private void timer_Elapsed(object sender, EventArgs e)
{
//Graphics graphic = Graphics.FromImage(pictureBox1.Image);
//graphic.RotateTransform(45);
// this.Invoke(new MethodInvoker(delegate { RotateImage(pictureBox1.Image, 10); }));
pictureBox1.Invalidate();
}
In your form you should enable this styles for a better perfomance and smoothness
SetStyle( ControlStyles.ResizeRedraw, true );
SetStyle( ControlStyles.UserPaint, true );
SetStyle( ControlStyles.AllPaintingInWmPaint, true );
SetStyle( ControlStyles.OptimizedDoubleBuffer, true );
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: