C# - Updating pictureBox.Image during Runtime causes Error - c#

i am trying to change the pictureBox.Image during Runtime. I have several Model classes with a picture stored, whenever i click on a MenuStripItem i call the method "ChangePictureBoxImages". Till then there is no error (the pB is invisible!) but once i call the method to make the pB visible i get an Error. The Error code: "An unhandled exception of type 'System.ArgumentException' occurred in System.Drawing.dll".
Research said i should dispose the picturebox and set it to "null", however this does NOT help.
My Code:
using (Image current = BitmapManipulator.EvaluateMesurement(CSV_Name1, max_Rows, max_Col, var.TopImage, var.BitmapToManipulate, pB_ColourScale_Evaluation.Image, var.BitmapToManipulate, var.Filepath, var.FoldID))
{
var.LastEvaluationImage = current;
BitmapManipulator.CombineImagesAndSaveThem_Evaluation(var.TopImage, var.BitmapToManipulate, pB_ColourScale_Evaluation.Image, var.Filepath, var.FoldID); //saves the Files as jpg
if (var.CurrentlyShownToUser) //checks if the MenuStripItem is the active one
{
if (var.LastEvaluationImage == null) { MessageBox.Show("the image is null");} //only for debugging purpose -> does never call
ChangePictureBoxImages();
}
}
and the ChangePictureBoxImages():
public void ChangePictureBoxImages()
{
foreach (Fold fold in FoldExisting)
{
if (fold.FoldID == LastSelectedMenuStripItem_Name) //the clicked item is the last Selected MenuStripItem
{
if (fold.LastEvaluationImage != null)
{
Debug.WriteLine(pB_Evaluation_Bottom.Image.ToString() + " " + fold.LastEvaluationImage.ToString());
pB_Evaluation_Bottom.Image = fold.LastEvaluationImage;
}
pB_Evaluation_Top.Image = fold.TopImage;
}
}
}
There is no error till then, the error appears once i call "pB_Evaluation_Bottom.visible = true". (or if i called the visible method first first the error appears upon changing the Image!) The error also appears upon clicking 2 times on the MenuStripItem. I load the picture from the Class Fold as following:
This will set an image in the fold class, this image will then be manipulated and stored in LastEvaluationImage
private void setTheImages(string PictureToManipulate, string PathToTopImage)
{
try
{
this.BitmapToManipulate_intern = (Image)Image.FromFile(#PictureToManipulate, true);
this.TopImage_intern = (Image)Image.FromFile(#PathToTopImage, true);
}
catch (ArgumentNullException ex)
{
Debug.WriteLine("The BitMap for the manipulation process and the top image is not created.");
}
}
and the LastEvaluationImage where the last picture is stored -> this will be called to be the new pb.Image
private Image LastEvaluationImage_intern;
public Image LastEvaluationImage
{
get
{
return this.LastEvaluationImage_intern;
}
set
{
if (LastEvaluationImage_intern != null) { LastEvaluationImage_intern.Dispose(); LastEvaluationImage_intern = null; }
this.LastEvaluationImage_intern = value;
this.LastEvaluationTime_intern = DateTime.Now;
}
}
I know this is a little complex, but i hope someone can help me.
THANKS IN ADVANCE!
UPDATE: The Error must be in the following Code:
The BitmapManipulator.EvaluateMeasurement Code :
public Image EvaluateMesurement(double[][] MeasuredValues, int max_Rows, int max_Col, Image pB_Evaluation_Top, Image pB_Evaluation_Bottom, Image pB_EvaluationColourScale, Image ManipulatedBitmap, string PathMeasurementFiles, string Foldname)
{
using (Bitmap bitmap = new Bitmap(ManipulatedBitmap))
{
// the data array sizes:
int number_nio = 0;
int number_total = 0;
List<FileInfo> LastFiles;
int got_number_for_trends = Properties.Settings.Default.TrendNumber;
SolidBrush myBrush = new SolidBrush(red);
using (Graphics g = Graphics.FromImage(bitmap))
{
Random rnd = new Random(8);
int[,] data = new int[max_Col, max_Rows];
// scale the tile size:
float sx = 1f * bitmap.Width / data.GetLength(0);
float sy = 1f * bitmap.Height / data.GetLength(1);
LastFiles = FM.GetLastFiles_Trend(ref got_number_for_trends, PathMeasurementFiles);
double[][] CSV_Statistiken = FM.LastFilesToCSV(got_number_for_trends, true, LastFiles, PathMeasurementFiles);
for (int x = 0; x < max_Col; x++)
{
for (int y = max_Rows - 1; y >= 0; y--)
{
number_total++;
RectangleF r = new RectangleF(x * sx, y * sy, sx, sy);
if (MeasuredValues[y][x] < Properties.Settings.Default.Threshhold)
{
number_nio++;
if (CSV_Statistiken[y][x] == Properties.Settings.Default.TrendNumber)
{
myBrush.Color = Color.FromArgb(150, black);
g.FillRectangle(myBrush, r);
}
else
{
myBrush.Color = Color.FromArgb(150, red);
g.FillRectangle(myBrush, r);
}
}
else
{
myBrush.Color = Color.FromArgb(150, green);
g.FillRectangle(myBrush, r);
}
}
}
}
return bitmap;
}
}
This returned bitmap will be stored in fold.LastEvaluationImage as following:
using (Image current = BitmapManipulator.EvaluateMesurement(CSV_Name1, max_Rows, max_Col, var.TopImage, var.BitmapToManipulate, pB_ColourScale_Evaluation.Image, var.BitmapToManipulate, var.Filepath, var.FoldID))
{
var.LastEvaluationImage = current;
}

You're returning a disposed bitmap. It shouldn't be surprising you can't draw something that no longer exists :)
The using (bitmap) is the last thing you want in this case. The bitmap must survive longer than the scope of the using. And the using (current) in the caller has the same problem - you're again disposing the image way too early. You can only dispose it when it's clear that it isn't going to be used ever again - e.g. when you replace it with a new image.
To elaborate, using does nothing but call Dispose when you leave its scope. In the case of Bitmap (which is just a "thin" wrapper around a GDI bitmap), this releases the memory where the actual image data is stored. There isn't anything interesting left, so there's nothing to draw (and you'd basically be calling DrawBitmap(NULL) as far as GDI is concerned).

Related

How to write data to .bmp file using an external program from unity?

I'm adding a feature to a project that will allow users to see a heat map representation of their mouse movements on the screen in real time. My goal is to make this API as dynamic as possible.
By dynamic, I mean I would like users to be able to use this API to generate the heat map in real time and then plug that heat map into their third party graphics software to view that heat map. (i.e Unity, React, Mobile, etc.. )
For testing purposes the third party graphics software that I am using is Unity.
I have created a .cs unity script that does the following per frame:
Start recording mouse locations.
Have ExternalProgram.exe generate a bitmap image using the
points that unity just recorded.
Then have Unity display the updated .bmp image on the screen.
Right now the problem that I am having is that the .bmp file is not being created when I use ProcessStartInfo in my unity script to run the .exe that is in charge of creating the .bmp image.
I've been debugging this code for the past week trying to figure out what is wrong with it. I know for a fact that Unity is successfully recording the mouse's location and passing those values to the .exe after calling ProcessStartInfo.
But for some reason it doesn't actually create the .bmp file. This is weird because if I independently run the ExternalProject in Visual Studio then it works just fine and it creates the .bmp file and shows the correct heat map representation on the image.
I figured that maybe starting a program and passing it tons of data and having that program create a file would be a lot of work for unity to do every single frame. (I am open to suggestions on ways to get around having to do that)
So I decided to just have the script record points for the first 15 seconds and then try to write that data to the .bmp file but that didn't work either.
Main program file for ExternalProject.exe
class Program
{
public static void Main(string[] args)
{
string arguments = "";
foreach (string arg in args)
{
arguments += arg;
}
Console.WriteLine("My Args: " + arguments + "--EOF");
bool noArguments = String.IsNullOrEmpty(arguments);
if (noArguments)
{
// Test input data
arguments = "(111,222)|(333,444)|(555,777)|(888,999)|(1000,1000)|(1000,1000)|(1000,1000)";
}
// The method ConvertStringToSignalsList() has already been tested and it works.
List<MouseMovementSignal> signals = ConvertStringToSignalsList(arguments);
CreateMouseHeatmap(signals);
Console.WriteLine("Press Enter to close...");
Console.ReadLine();
}
private static void CreateMouseHeatmap(List<MouseMovementSignal> argumentSignals)
{
int Height = 2100;
int Width = 3800;
List<MouseMovementSignal> mouseSignals
= argumentSignals; // Program perameters
//= getRecordedMouseData(); // DB
//= createFakeMouseSignals(Height, Width); // Generates fake signals
try
{
HeatmapStructure<MouseMovementSignal> mapper =
new HeatmapStructure<MouseMovementSignal>(Height, Width);
mapper.LoadSignalsFromObjects(mouseSignals);
// .BMP Image is created inside of this method !!
mapper.IdentifyHeatRegions();
//MouseMovementSignal mms = argumentSignals[argumentSignals.Count - 1];
//Console.WriteLine("Last: " + mms.Channels[0].Name + ": " + mms.Channels[0].Values[0] + ", "
// + mms.Channels[1].Name + ": " + mms.Channels[1].Values[0]);
//finalHeatmap.Save("MyFirstBitmap.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
Console.WriteLine("Image actually Complete!!!!!!!!!!!!!!");
}
catch (Exception e)
{
Console.WriteLine("Error: " + e);
}
}
}
My Unity Script
public class HeatmapScript : MonoBehaviour {
private List<string> points;
private Camera cam;
private RawImage ri;
private string heatmapBmpPath = #"C:\Users\not-showing-you-my-file-structure-LOL\MyFirstBitmap.bmp";
private int frameCount = 0;
string pointsAsString = "";
Stopwatch sw;
// Use this for initialization
void Start() {
// Initialize the raw image.
cam = Camera.main;
GameObject imageObject = GameObject.FindGameObjectWithTag("imageView");
ri = imageObject.GetComponent<RawImage>();
// Initialize points list.
points = new List<string>();
sw = new Stopwatch();
sw.Start();
}
bool stop = false;
// Update is called once per frame.
void Update() {
float xValue = Input.mousePosition.x;
float yValue = Input.mousePosition.y;
Vector2 newPoint = new Vector2(xValue, yValue);
points.Add(newPoint.ToString());
int tSecs = 15000;// 15 seconds
// After 15 seconds of recording points pass them to the program that creates the heat map.
if (stop == false && sw.ElapsedMilliseconds > tSecs)
{
StartCoroutine("UpdateBMP");
UnityEngine.Debug.Log(points.Count);
stop = true;
}
//Update the raw image on the screen.
UnityEngine.Texture2D newTexture = CreateTextureFromBitmap(heatmapBmpPath);
//Set the RawImage to the size of the scren.
ri.texture = newTexture;
ri.rectTransform.sizeDelta = new Vector2(Screen.width, Screen.height);
frameCount++;
}
IEnumerator UpdateBMP()
{
// Show mouse position in unity environment
float xValue = Input.mousePosition.x;
float yValue = Input.mousePosition.y;
Vector2 newPoint = new Vector2(xValue, yValue);
points.Add(newPoint.ToString());
// EX:
// (123,123)|(123,123)|(123,123)|(123,123)|(123,123)|(123,123)
// display list contents without loop
pointsAsString = string.Join("|", points.ToArray());
// Every frame call Behavior's Program.cs that calls HeatmapStructure.cs to update .bmp file
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.FileName = #"C:\Users\not-showing-you-my-file-structure-LOL\ExternalProgram.exe";
processInfo.UseShellExecute = false;
processInfo.Arguments = pointsAsString;
Process process = Process.Start(processInfo);
yield return null;
}
private UnityEngine.Texture2D CreateTextureFromBitmap(string completeFilePath)
{
BMPLoader loader = new BMPLoader();
BMPImage img = loader.LoadBMP(completeFilePath);
UnityEngine.Texture2D myTexture = img.ToTexture2D();
UnityEngine.Debug.Log("File Size: " + img.header.filesize);
return myTexture;
}
}
HeatmapStructure.cs class
public class HeatmapStructure<T> where T : ISignal
{
public class COGPoint
{
public double X, Y, Z;
//public Color Color;
public byte Intensity;
public bool isD3Point = false; // 3D Point check
public const double DEFAULT_AXIS_LOC = 0.0001;
public COGPoint()
{
//Color = Color.Blue;
Intensity = 0;
}
// NOTE: double z has a default value therefore it is optional
public COGPoint(byte intensity, double x, double y, double z = DEFAULT_AXIS_LOC)
{
this.X = x;
this.Y = y;
this.Z = z; // Optional
//Color = Color.Blue; // Cold: Blue / Hot: Red
this.Intensity = intensity;
if (z != DEFAULT_AXIS_LOC)
{
isD3Point = true;
}
}
public override string ToString()
{
string output = (isD3Point == true) ?
("(x,y,z) " + X + "," + Y + "," + Z) : ("(x,y) " + X + "," + Y); // 3D : 2D
output += //" Color: " + Color.ToString() +
" Intensity: " + Intensity;
return output;
}
}
private List<COGPoint> points;
private int Height;
private int Width;
public HeatmapStructure(int Height, int Width)
{
this.Height = Height;
this.Width = Width;
points = new List<COGPoint>();
}
private Bitmap CreateIntensityMask(Bitmap bSurface, List<COGPoint> aHeatPoints)
{
// Create new graphics surface from memory bitmap
Graphics DrawSurface = Graphics.FromImage(bSurface);
// Set background color to white so that pixels can be correctly colorized
DrawSurface.Clear(Color.White);
// Traverse heat point data and draw masks for each heat point
foreach (COGPoint DataPoint in aHeatPoints)
{
// Render current heat point on draw surface
DrawHeatPoint(DrawSurface, DataPoint, 45);
}
return bSurface;
}
// TODO: How to draw updating Bitmap in unity in real time ??
private void DrawHeatPoint(Graphics Canvas, COGPoint HeatPoint, int Radius)
{
// Create points generic list of points to hold circumference points
List<Point> CircumferencePointsList = new List<Point>();
// Create an empty point to predefine the point struct used in the circumference loop
Point CircumferencePoint;
// Create an empty array that will be populated with points from the generic list
Point[] CircumferencePointsArray;
// Calculate ratio to scale byte intensity range from 0-255 to 0-1
float fRatio = 1F / Byte.MaxValue;
// Precalulate half of byte max value
byte bHalf = Byte.MaxValue / 2;
// Flip intensity on it's center value from low-high to high-low
int iIntensity = (byte)(HeatPoint.Intensity - ((HeatPoint.Intensity - bHalf) * 2));
// Store scaled and flipped intensity value for use with gradient center location
float fIntensity = iIntensity * fRatio;
// Loop through all angles of a circle
// Define loop variable as a double to prevent casting in each iteration
// Iterate through loop on 10 degree deltas, this can change to improve performance
for (double i = 0; i <= 360; i += 10)
{
// Replace last iteration point with new empty point struct
CircumferencePoint = new Point();
// Plot new point on the circumference of a circle of the defined radius
// Using the point coordinates, radius, and angle
// Calculate the position of this iterations point on the circle
CircumferencePoint.X = Convert.ToInt32(HeatPoint.X + Radius * Math.Cos(ConvertDegreesToRadians(i)));
CircumferencePoint.Y = Convert.ToInt32(HeatPoint.Y + Radius * Math.Sin(ConvertDegreesToRadians(i)));
// Add newly plotted circumference point to generic point list
CircumferencePointsList.Add(CircumferencePoint);
}
// Populate empty points system array from generic points array list
// Do this to satisfy the datatype of the PathGradientBrush and FillPolygon methods
CircumferencePointsArray = CircumferencePointsList.ToArray();
// Create new PathGradientBrush to create a radial gradient using the circumference points
PathGradientBrush GradientShaper = new PathGradientBrush(CircumferencePointsArray);
// Create new color blend to tell the PathGradientBrush what colors to use and where to put them
ColorBlend GradientSpecifications = new ColorBlend(3);
// Define positions of gradient colors, use intesity to adjust the middle color to
// show more mask or less mask
GradientSpecifications.Positions = new float[3] { 0, fIntensity, 1 };
// Define gradient colors and their alpha values, adjust alpha of gradient colors to match intensity
GradientSpecifications.Colors = new Color[3]
{
Color.FromArgb(0, Color.White),
Color.FromArgb(HeatPoint.Intensity, Color.Black),
Color.FromArgb(HeatPoint.Intensity, Color.Black)
};
// Pass off color blend to PathGradientBrush to instruct it how to generate the gradient
GradientShaper.InterpolationColors = GradientSpecifications;
// Draw polygon (circle) using our point array and gradient brush
Canvas.FillPolygon(GradientShaper, CircumferencePointsArray);
}
private double ConvertDegreesToRadians(double degrees)
{
double radians = (Math.PI / 180) * degrees;
return (radians);
}
// old name : button1_Click
public Bitmap IdentifyHeatRegions()
{
// Create new memory bitmap the same size as the picture box
Bitmap bMap = new Bitmap(Width, Height);
// Call CreateIntensityMask, give it the memory bitmap, and use it's output to set the picture box image
Bitmap bm = CreateIntensityMask(bMap, points);
Bitmap coloredBitmap = Colorize(bm, 243); // <-- NOTE: should be 255. But my palette.bmp is 243x5
coloredBitmap.Save("MyFirstBitmap.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
return coloredBitmap;
}
public static Bitmap Colorize(Bitmap Mask, byte Alpha)
{
// Create new bitmap to act as a work surface for the colorization process
Bitmap Output = new Bitmap(Mask.Width, Mask.Height, PixelFormat.Format32bppArgb);
// Create a graphics object from our memory bitmap so we can draw on it and clear it's drawing surface
Graphics Surface = Graphics.FromImage(Output);
Surface.Clear(Color.Transparent);
// Build an array of color mappings to remap our greyscale mask to full color
// Accept an alpha byte to specify the transparancy of the output image
ColorMap[] Colors = CreatePaletteIndex(Alpha);
// Create new image attributes class to handle the color remappings
// Inject our color map array to instruct the image attributes class how to do the colorization
ImageAttributes Remapper = new ImageAttributes();
try
{
Remapper.SetRemapTable(Colors);
}
catch (Exception e)
{
Console.WriteLine(e);
}
// Draw our mask onto our memory bitmap work surface using the new color mapping scheme
Surface.DrawImage(Mask, new Rectangle(0, 0, Mask.Width, Mask.Height), 0, 0, Mask.Width, Mask.Height, GraphicsUnit.Pixel, Remapper);
// Send back newly colorized memory bitmap
return Output;
}
private static ColorMap[] CreatePaletteIndex(byte Alpha)
{
ColorMap[] OutputMap = new ColorMap[Alpha + 1];
// Change this path to wherever you saved the palette image.
Bitmap Palette = (Bitmap)Bitmap.FromFile(#"C:\Users\cdowns\Desktop\palette.bmp");
// Loop through each pixel and create a new color mapping
try
{
for (int X = 0; X <= Alpha; X++)
{
OutputMap[X] = new ColorMap();
OutputMap[X].OldColor = Color.FromArgb(X, X, X);
OutputMap[X].NewColor = Color.FromArgb(Alpha, Palette.GetPixel(X, 0));
}
}
catch (Exception e) {
Console.WriteLine(e);
}
return OutputMap;
}
public void LoadSignalsFromObjects(List<T> allSignals) // ISignal Object
{
Random random = new Random();
foreach (T signal in allSignals)
{
COGPoint newPoint = new COGPoint();
if (allSignals[0].GetType() == typeof(MouseMovementSignal))
{
string axis1 = signal.Channels[0].Name;
List<double> value1 = signal.Channels[0].Values;
string axis2 = signal.Channels[1].Name;
List<double> value2 = signal.Channels[1].Values;
// Make sure to enter signals into database in this format
//Console.WriteLine(axis1 + " " + axis2);
newPoint.X = value1[0];
newPoint.Y = value2[0];
// TOOD: Implement
newPoint.Intensity = (byte)random.Next(0, 120);
// Display newPoint values
//Console.WriteLine("COGPoint Numbers: X: " + newPoint.X + " , Y: " + newPoint.Y
// + /*" Color: " + newPoint.Color + */" Intensity: " + newPoint.Intensity);
}
else if (allSignals[0].GetType() == typeof(EyePosition))
{
}
// Add to main list of heat points
points.Add(newPoint);
}
}
}
Expected result is that the .bmp image is created after the first 15 seconds.
(P.S. I am very new to both Unity and C# to I am probably doing this completely wrong. I am open to an entirely new idea for going about making this work. Thanks)
After doing a little more browsing on here for similar problems that other people have had I found this post pictureBox.Image.Save() in c# doesn't work. It answered my question regarding why my .bmp wasn't being generated.
It turns out that my program was working correctly after all. It was correctly generating the .bmp file. However, when I called ProcessStartInfo from within unity to run the ExternalProgram.exe that called Bitmap.save("filename.bmp") the working directory changed. Therefore, the image was not being saved in the location that I was expecting to find it in.

Attempting to draw rectangles multithreaded

I am attempting to draw about 3600 points on a form, it is pretty slow using one thread so I decided I want to use 4 threads for it.
In my code I divide the 3600 points to the 4 threads and they are supposed to draw it. however for some reason an ArgumentOutOfRangeException is being thrown.
I tried to debug my code but I couldn't find the mistake.
here is the code :
(Ignore the class _3DPoint, it is just a point that has x,y,z values. when I draw them I only use the x,y values.)
code for drawing the points :
public Graphics g; //g = this.CreateGraphics() in form1.Load()
public void drawrectangle(_3DPoint)
float xCord = float.Parse(p.x.ToString());
float yCord = float.Parse(p.y.ToString());
Brush b = new SolidBrush(Color.White);
xCord = lsize * xCord + center.X;
yCord = lsize * yCord + 10 + center.Y;
g.FillRectangle(b, xCord, yCord, 2, 2);
}
lsize, center are just variables for aligning the points as I want them.
All of the multithread action code:
public List<_3DPoint[]> multiThreadsdata = new List<_3DPoint[]>();
public void handlemultithread(_3DPoint[] P)
{
g.Clear(Color.Black);
for (int i = 0; i < multiThreads.Length; i++)
{
multiThreadsdata.Add(new _3DPoint[P.Length / multiThreads.Length]);
}
for (int i = 0; i < multiThreads.Length; i++)
{
for (int j = (P.Length / multiThreads.Length) * (i); j < (P.Length / multiThreads.Length) * (i + 1); j++)
{
multiThreadsdata[i][j - ((P.Length / multiThreads.Length) * i)] = new _3DPoint(P[j]);
}
}
for (int i = 0; i < multiThreads.Length; i++)
{
multiThreads[i] = new Thread(() => drawPoints(multiThreadsdata[i]));
multiThreads[i].Start();
}
}
delegate void SetCallBackPoint(_3DPoint location);
public void drawPoints(_3DPoint[] locations)
{
for (int i = 0; i < locations.Length; i++)
{
if (this.InvokeRequired)
{
SetCallBackPoint e = new SetCallBackPoint(drawrectangle);
this.Invoke(e, new object[] { locations[i] });
}
else
{
drawrectangle(locations[i]);
}
}
}
P is a _3DPoint array that contains all the 3600 points.
mutliThreads is a Thread[] containing 4 threads.
I get the exception in handlemultithread method. in the third line of this for loop :
for (int i = 0; i < multiThreads.Length; i++)
{
multiThreads[i] = new Thread(() => drawPoints(multiThreadsdata[i])); // <- here.
multiThreads[i].Start();
}
I don't know what is the problem, my guess is that there is some problem with the multithreading because I'm just a beginner with multithreading.
Thanks a bunch.
It is entirely possible to Draw 3600 rectangles quickly on a form when you apply the suggestions in the comments.
If that doesn't give you enough time you can consider creating Images on a single background thread, store them in some sort of buffer until they are needed to e painted on the Graphics object of the Paint event of the form. That is only feasible if you can know upfront what needs to be painted on the next frame.
This example uses a simple Background worker to fill an ConcurrentQueue with images. The comments in the code explain what is going on.
public partial class Form1 : Form
{
static ConcurrentQueue<Image> buffer = new ConcurrentQueue<Image>();
static Random r = new Random();
public Form1()
{
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
// this is already a great performance win ...
DoubleBuffered = true;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Image img =null;
// get from buffer ..
if (!buffer.TryDequeue(out img))
{
// nothing available
// direct random
for (var x = 0; x < e.ClipRectangle.Width; x++)
{
for (var y = 0; y < e.ClipRectangle.Height; y++)
{
using (var pen = new Pen(new SolidBrush(Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)))))
{
e.Graphics.DrawRectangle(pen, x, y, 1, 1);
}
}
}
}
else
{
// otherwise Draw the prepared image
e.Graphics.DrawImage(img,0,0);
Trace.WriteLine(buffer.Count);
img.Dispose();
}
}
private void button1_Click(object sender, EventArgs e)
{
// force a repaint of the Form
Invalidate();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// as long as the form is not disposed
while (!IsDisposed)
{
// we keep 60 images in memory
if (buffer.Count < 60)
{
// bitmap
var bmp = new Bitmap(this.Width, this.Height);
var img = Graphics.FromImage(bmp);
// draw
for (int i = 0; i < 3600; i++)
{
using (var pen = new Pen(new SolidBrush(Color.FromArgb(r.Next(255), r.Next(255), r.Next(255)))))
{
img.DrawRectangle(pen, r.Next(Width),r.Next(Height), r.Next(Width), r.Next(Height));
}
}
// store the drawing in the buffer
buffer.Enqueue(bmp);
}
else
{
// simple and naive way to give other threads a bit of room
Thread.Sleep(0);
}
}
}
}
Keep in mind that when you have a CPU heavy process adding more threads will not by magic make your methods run quicker. You might even make it worse: more threads compete for time on the CPU.

WinForms Bitmap region is already locked

I'm trying to slide through a few images basically you have 2 buttons forward and backwards. They're function is to scroll through a list images. Once one of them reaches the end it must go back to the other side of the list. Here's what I have
private List<Bitmap> RotatePacks = new List<Bitmap> { new Bitmap(#"Assets\All_Cards\All_Royal\All_Royal.png"),
new Bitmap(#"Assets\All_Cards\All_Classic\All_Classic.jpg")};
private void bNext_Click(object sender, EventArgs e)
{
Bitmap currentImage = (Bitmap)pickCards.Image;
for (int i = 0; i < RotatePacks.Count; i++)
{
if (AreEqual(currentImage, RotatePacks[i]))
{
try
{
pickCards.Image = RotatePacks[i + 1];
}
catch (Exception)
{
bPrevious_Click(sender, e);
pickCards.Image = RotatePacks[i - 1];
}
}
}
}
private void bPrevious_Click(object sender, EventArgs e)
{
Bitmap currentImage = (Bitmap)pickCards.Image;
for (int i = 0; i < RotatePacks.Count; i++)
{
if (AreEqual(currentImage, RotatePacks[i]))
{
try
{
pickCards.Image = RotatePacks[i - 1];
}
catch (Exception)
{
bNext_Click(sender, e);
}
}
}
}
Those are the 2 buttons. Here I'm trying to compare the image of the pictureBox that's holding the images with the list RotatePacks. Like this im getting the current image that is being shown. Here's the AreEqual method :
public unsafe static bool AreEqual(Bitmap b1, Bitmap b2) // copy pasted
{
if (b1.Size != b2.Size)
{
return false;
}
if (b1.PixelFormat != b2.PixelFormat)
{
return false;
}
/*if (b1.PixelFormat != PixelFormat.Format32bppArgb)
{
return false;
}*/
Rectangle rect = new Rectangle(0, 0, b1.Width, b1.Height);
BitmapData data1
= b1.LockBits(rect, ImageLockMode.ReadOnly, b1.PixelFormat);
BitmapData data2
= b2.LockBits(rect, ImageLockMode.ReadOnly, b1.PixelFormat);
int* p1 = (int*)data1.Scan0;
int* p2 = (int*)data2.Scan0;
int byteCount = b1.Height * data1.Stride / 4; //only Format32bppArgb
bool result = true;
for (int i = 0; i < byteCount; ++i)
{
if (*p1++ != *p2++)
{
result = false;
break;
}
}
b1.UnlockBits(data1);
b2.UnlockBits(data2);
return result;
}
So now back to my problem the buttons are working just the way i want them but they work just once. If i press button next and than button previous or i press the next button twice the program will crash. It gives me the exception here
BitmapData data2
= b2.LockBits(rect, ImageLockMode.ReadOnly, b1.PixelFormat);
Here's some screenshots of the actual exception :
http://prntscr.com/9ug3nl
http://prntscr.com/9ug3vv
P.S Im using this comparing method but i haven't program it. I copied the code from another StackOverflow question
There seems to be a couple of issues here:
It is worth to have an early check
if (b1 == b2) //put this
return true;
//do something else
Rectangle rect = new Rectangle(0, 0, b1.Width, b1.Height);
//and so on
Likely b1 == b2 will cause that problem
Seems like your LockBits refer to the same exact items (same rect, same size, some mode, same pixel format):
Rectangle rect = new Rectangle(0, 0, b1.Width, b1.Height);
BitmapData data1
= b1.LockBits(rect, ImageLockMode.ReadOnly, b1.PixelFormat);
BitmapData data2
= b2.LockBits(rect, ImageLockMode.ReadOnly, b1.PixelFormat);
This could be another cause of the issue...
Like Ian pointed out you have to check if the images you are trying to compare in AreEqual() method (the current image and the one in Rotatepacks[i]) are identical AND belong to the same instance or not? IF they belong to the same instance it'd obviously generate that exception. Use the .Equals() method to check if the images belong to the same instance.
Consider an image instance
Bitmap img;
The image in img is loaded into the picture box as well as in the RotatePacks. In your code you are trying to lock that same instance twice so it generates an exception
The bitmap lines Ian pointed are ok tho.., 2 different images can have the same size and pixel format.
Also if that really was the case, i don't think you need to compare pixel by pixel. I suppose you have Bitmap instances in Rotatepacks. Just compare your image instance in picture box with the one in Rotatepacks using .Equals()

Printing an array of images

I inherited an app that needs to print an array of images which are stored in a database. I have written the code to retrieve the images via a web service, and have put the images into an array of BitmapImage.
However when I add the BitmapImage to the source of the Image, the image is then empty. No exceptions are thrown, but it appears that no images are added to the list, and the code appears to simply halt as soon as I add the image to the array.
Any hints would be appreciated.
In header of class:
\\BitmapImage is System.Windows.Media.Imaging.BitmapImage
public List<BitmapImage> prtImages;
Event handler for web service on completion of getting images asynchronously:
void client_GetImagesCompleted(object sender, OrderImageWebService.GetOrderImagesCompletedEventArgs e)
{
if (e.Error != null)
{
System.Windows.Browser.HtmlPage.Window.Eval("alert('Error occurred loading images.');");
throw new Exception(string.Format("Error loading image edits: {0}", e.Error));
}
else if ((!e.Cancelled) && (e.Result != null))
{
try
{
for (int i = 0; i <= imgs; i++)
{
byte[] bImage = loi[i].Content;
using (System.IO.MemoryStream sioms =
new System.IO.MemoryStream(bImage, 0, bImage.Length))
{
bi.SetSource(sioms);
int ph = bi.PixelHeight; //Alert shows 3296
int pw = bi.PixelWidth; //Alert shows 2560
System.Windows.Browser.HtmlPage.Window.Eval(
string.Format("alert('BI image is {0} by {1}.');",
ph.ToString(), pw.ToString()));
prtImages.Add(bi);
//This alert never shows
System.Windows.Browser.HtmlPage.Window.Eval(
string.Format("alert('prtImages has size {1} after add.');",
prtImages.Count.ToString()));
}
}
}
catch(Exception ex)
{
//This exception is never caught
System.Windows.Browser.HtmlPage.Window.Eval(string.Format("alert('{0}');", ex.Message));
}
}
else
{
System.Windows.Browser.HtmlPage.Window.Eval("alert('Oops. Error.');");
}
System.Windows.Browser.HtmlPage.Window.Eval("alert('Clearing wait.');");
bPrtImageLoaded = true;
ewh_gotImages.Set();
}
In GetPrintImage:
public Image GetPrintImage(int iPageID)
{
if (bPrtImageLoaded)
{
Image img = new Image();
img.Source = prtImages[iPageID];
double ah = img.ActualHeight;
double aw = img.ActualWidth;
System.Windows.Browser.HtmlPage.Window.Eval(
string.Format("alert('Image is {0} by {1}.');",
ah.ToString(), aw.ToString()));
if (null != img)
{
return (img);
}
}
return(null);
}
Called from button click
private void p_printMultiplePages(object sender, System.Windows.Printing.PrintPageEventArgs e)
{
Image img = GetPrintImage(iCurPrintPage);
if (null != img)
{
if (e.PrintableArea.Height < img.ActualHeight)
{
scale = e.PrintableArea.Height / img.ActualHeight;
}
if ((e.PrintableArea.Width < img.ActualWidth) && ((e.PrintableArea.Width / img.ActualWidth) < scale))
{
scale = e.PrintableArea.Width / img.ActualWidth;
}
if (scale < 1)
{
scaleTransform.ScaleX = scale;
scaleTransform.ScaleY = scale;
img.RenderTransform = scaleTransform;
}
e.PageVisual = img;
iCurPrintPage++;
if (iCurPrintPage < SessionState.PageCount)
{
e.HasMorePages = true;
}
else
{
e.HasMorePages = false;
}
}
else
{
System.Windows.Browser.HtmlPage.Window.Eval(
string.Format("alert('Image {0} of order {1} is null.');",
iCurPrintPage.ToString(), SessionState.OrderID.ToString()));
e.HasMorePages = false;
}
}
Thanks for any hints you might have,
Bruce.
I would suggest getting images from web service as binary data (byte[] ) encoded each image as base64 and publish array of base64 strings. On the client you will decode each element of array with base64 stings back to byte[]. Read it with MemoryStream and pass that object to your Image reader (read this one reading image from bytes)
But before you do anything of that, make sure that your p_printMultiplePages method can display local images

Why is DrawRectangle drawing a cross inside my PictureBox

I'm trying to draw 10 rectangles, but when I use g.DrawRectangle() it is drawing a cross as shown below:
I'm creating Vertex objects that contain a getRectangle() function which returns a Rectangle object for that vertex.
I was hoping to create these objects and show them as Rectangles on the pictureBox.
Here's my code
private System.Drawing.Graphics g;
private System.Drawing.Pen pen1 = new System.Drawing.Pen(Color.Blue, 2F);
public Form1()
{
InitializeComponent();
pictureBox.Dock = DockStyle.Fill;
pictureBox.BackColor = Color.White;
}
private void paintPictureBox(object sender, PaintEventArgs e)
{
// Draw the vertex on the screen
g = e.Graphics;
// Create new graph object
Graph newGraph = new Graph();
for (int i = 0; i <= 10; i++)
{
// Tried this code too, but it still shows the cross
//g.DrawRectangle(pen1, Rectangle(10,10,10,10);
g.DrawRectangle(pen1, newGraph.verteces[0,i].getRectangle());
}
}
Code for Vertex class
class Vertex
{
public int locationX;
public int locationY;
public int height = 10;
public int width = 10;
// Empty overload constructor
public Vertex()
{
}
// Constructor for Vertex
public Vertex(int locX, int locY)
{
// Set the variables
this.locationX = locX;
this.locationY = locY;
}
public Rectangle getRectangle()
{
// Create a rectangle out of the vertex information
return new Rectangle(locationX, locationY, width, height);
}
}
Code for Graph class
class Graph
{
//verteces;
public Vertex[,] verteces = new Vertex[10, 10];
public Graph()
{
// Generate the graph, create the vertexs
for (int i = 0; i <= 10; i++)
{
// Create 10 Vertexes with different coordinates
verteces[0, i] = new Vertex(0, i);
}
}
}
Looks like an exception in your draw loop
last call to:
newGraph.verteces[0,i]
fails with OutOfRangeException
you shoul iterate not to i <= 10, but to i < 10
Red Cross Indicates that an Exception has been thrown, you are not seeing it because it's being handled. Configure Visual Studio to break on exception throw to catch it.
An exception has been thrown. At first look your code:
for (int i = 0; i <= 10; i++)
will generate an IndexOutOfRangeException because verteces has 10 items but it will cycle from 0 to 10 (included so it'll search for 11 elements). It depends on what you want to do but you have to change the cycle to (removing the = from <=):
for (int i = 0; i < 10; i++)
or to increment the size of verteces to 11.

Categories

Resources