-2

I'm doing all my expensive calculations in a background thread and need to be able to assign pictureBox1.Image after each cycle of calculations. I use to do this using delegates, but its been a long time and things don't seem to work that way anymore.

I tried putting the assignment in a WorkerCompleted event handler, but then I can't figure out how to restart backgroundWorker1. I've been at this all day and feel enormously frustrated, this use to be so easy to do.

This is the most relevant part of the code:

using System;
using System.Globalization;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using KnownColorsPalette;

namespace Color_Visualizer
{
    public partial class Form1 : Form
    {
        static CultureInfo m_culture = CultureInfo.CurrentCulture;
        double[,][] distances = new double[3000, 3000][];
        FastPixel m_fp;

        public Form1()
        {
            InitializeComponent();
            WindowState = FormWindowState.Maximized;
            this.pictureBox1.LoadCompleted += new System.ComponentModel.AsyncCompletedEventHandler(PictureBox1_LoadCompleted);
            CoordinateSystem.AssignMe(this);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ReadColors(); // Reads text file alternating name and RGB hex of
                          // Wikipedia's 1200+ named colors.

            Point3D p = new Point3D(127.5, 127.5, 127.5);
            foreach (FoundColors fc in m_lColors.Values)
                fc.pt = fc.color - p;

            Coord = new CoordinateSystem(new Plane(new Point3D(-127.5, -127.5, -127.5), new Point3D(-1, 0, 0)));
            backgroundWorker1.RunWorkerAsync();
        }


        double fSpeed = 5;
        Point3D m_pNormal = new Point3D();

        private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            double fAngle, fRadius, d;
            Point3D vU, vV;
            Point pLocation = new Point();

            m_pNormal = Coord.Plane.Normal;

            fAngle = Math.Atan2(m_pNormal.y, m_pNormal.x);
            fRadius = Math.Sqrt(m_pNormal.x * m_pNormal.x + m_pNormal.y * m_pNormal.y);
            fAngle += fSpeed * 180 / Math.PI;
            m_pNormal.x = Math.Cos(fAngle) * fRadius;
            m_pNormal.y = Math.Sin(fAngle) * fRadius;

            m_fp.Lock();
            m_fp.Clear(Color.Black);
            foreach (FoundColors fc in m_lColors.Values)
            {
                vU = new Point3D(Coord.U);
                d = dist(fc.pt, ref vU);
                vV = Coord.V;
                vV.mult(d);

                pLocation.X = (int)(m_midHoriz + vU.norm());
                pLocation.Y = (int)(m_midVert + vV.norm());
                m_fp.SetPixel(pLocation, fc.color);
            }
            m_fp.Unlock();
        }


        double m_fDist, m_fDot;
        public double dist(Point3D pt, ref Point3D vU)
        {
            double c1 = pt.dot(vU);
            double c2 = vU.dot(vU);
            double b = c1 / c2;
            vU.mult(b);
            m_fDot = pt.dot(Coord.Normal);
            m_fDist = pt.norm(pt - vU);
            return m_fDist;
        }

        double m_midHoriz, m_midVert;

        private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            pictureBox1.Image = m_fp.Bitmap;
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            m_midHoriz = pictureBox1.Width / 2;
            m_midVert = pictureBox1.Height / 2;
            m_fp = new FastPixel(new Bitmap(pictureBox1.Width, pictureBox1.Height));
        }
    }
}

And this is part of my support code:

using System;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;

namespace Color_Visualizer
{

    public partial class Form1 : Form
    {
        class CoordinateSystem
        {
            const int MAX = 256;
            const double PlaneWidth = 600;

            static Form1 Me;
            static Point3D axisZ = new Point3D(0, 0, 1);
            static Point3D axisY = new Point3D(0, 1, 0);
            private Plane m_plane = new Plane(new Point3D(128, 128, 128), new Point3D(-128, 0, 0));
            private Point3D m_pV = new Point3D(0, 0, 0);
            private Point3D m_pU = new Point3D(0, 0, 0);
            private double m_fInc;

            public CoordinateSystem(Plane axAxis)
            {
                m_fInc = PlaneWidth / Me.ClientSize.Height;
                Plane = axAxis;
            }
            public static void AssignMe(Form1 form) { Me = form; }
            public Point3D U { get { return m_pU; } protected set { m_pU = value; } }
            public Point3D V { get { return m_pV; } protected set { m_pV = value; } }
            public Point3D Normal { get { return m_plane.Normal; } set { m_plane.Normal = value; } }
            static double COSerror = 0.99619469809174553229501040247389;

            public Plane Plane
            { 
                get { return m_plane; }
                set {
                    m_plane = value;
                    if (m_plane.dot(axisZ) > COSerror)
                        U = U.cross(m_plane, axisY);
                    else
                        U = U.cross(m_plane, axisZ);
                    U.div(U.norm());
                    V = U.cross(U, m_plane);
                    V.div(V.norm());
                }
            }
        }

        [DebuggerDisplayAttribute("x = {x}, y = {y}, z = {z}")]
        public class Point3D
        {
            public double x, y, z;

            public Point3D(double _x, double _y, double _z) { x = _x; y = _y; z = _z; }
            public Point3D(Point3D p) { x = p.x; y = p.y; z = p.z; }
            public Point3D() { x = 0; y = 0; z = 0; }
            public bool Equals(Point3D p) { return x == p.x & y == p.y & z == p.z; }
            public override bool Equals(object obj) { return Equals((Point3D)obj); }
            public static bool operator ==(Point3D p1, Point3D p2) { return p1.Equals(p2); }
            public static bool operator !=(Point3D p1, Point3D p2) { return !p1.Equals(p2); }
            public static Point3D operator -(Point3D e, Point3D s) { return new Point3D(e.x - s.x, e.y - s.y, e.z - s.z); }
            public static Point3D operator +(Point3D e, Point3D s) { return new Point3D(e.x + s.x, e.y + s.y, e.z + s.z); }
            public static Point3D operator *(double m, Point3D v) { return new Point3D(m * v.x, m * v.y, m * v.z); }
            public static Point3D operator *(Point3D v, double m) { return new Point3D(v.x / m, v.y / m, v.z / m); }
            public static Point3D operator /(double m, Point3D v) { return new Point3D(m * v.x, m * v.y, m * v.z); }
            public static Point3D operator /(Point3D v, double m) { return new Point3D(v.x / m, v.y / m, v.z / m); }
            public static implicit operator Color(Point3D p) { return Color.FromArgb((int)p.x, (int)p.y, (int)p.z); }
            public static implicit operator Point3D(Color c) { return new Point3D(c.R, c.G, c.B); }
            //public override int GetHashCode()
            //{
            //    unchecked
            //    {
            //        var hash = new SpookyHash();
            //        hash.Update(x);
            //        hash.Update(y);
            //        hash.Update(z);
            //        return hash.Final().GetHashCode();
            //    }
            //}

            // dot product (3D) which allows vector operations in arguments
            public double dot(Point3D u, Point3D v) { return u.x * v.x + u.y * v.y + u.z * v.z; }
            public double dot(Point3D u) { return u.x * x + u.y * y + u.z * z; }
            public double norm(Point3D v) { return Math.Sqrt(dot(v, v)); }     // norm = length of  vector
            public double norm() { return Math.Sqrt(dot(this, this)); }     // norm = length of  vector
            public double dist(Point3D u, Point3D v) { return norm(u - v); }         // distance = norm of difference
            public double dist(Point3D u) { return norm(this - u); }
            public Point3D cross(Point3D u, Point3D v) { return new Point3D(u.y * v.z - u.z * v.y, u.z * v.x - u.x * v.z, u.x * v.y - u.y * v.x); }
            public Point3D cross(Point3D u) { return new Point3D(u.y * z - u.z * y, u.z * x - u.x * z, u.x * y - u.y * x); }
            public void add(Point3D p) { x += p.x; y += p.y; z += p.z; }
            public void mult(double m) { x *= m; y *= m; z *= m; }
            public void div(double m) { x /= m; y /= m; z /= m; }
        }


        class Plane : Point3D
        {
            Point3D m_pNormal;

            public Plane(Point3D pOrigin, Point3D pNormal) : base(pOrigin) { m_pNormal = pNormal; }
            public Plane(Point3D p) : base(p) { }
            public Plane(double x, double y, double z) : base(x, y, z) { }
            public Point3D Normal { get { return m_pNormal; } set { m_pNormal = value; } }
            public double PointToPlane(Point3D p) { return p.dot(Normal); }
        }


        private CoordinateSystem m_coordSys;
        private CoordinateSystem Coord
        {
            get { return m_coordSys; }
            set { m_coordSys = value; }
        }
    }
}

And more support code:

    [DebuggerDisplayAttribute("{name}, R={color.R}, G={color.G}, B={color.B}")]
    class FoundColors
    {
        public Color color = Color.Empty;
        public string name = "";
        public CIELab_Color cLab;
        public Point3D pt;
        public List<int> lClosest = new List<int>();
        public int nFarthest;
        public FoundColors(FoundColors fc)
        {
            color = fc.color;
            name = fc.name;
            cLab = new CIELab_Color(fc.cLab.CIE_L, fc.cLab.CIE_a, fc.cLab.CIE_b);
            lClosest.AddRange(fc.lClosest);
            nFarthest = fc.nFarthest;
        }
        public FoundColors() { }
    }
  • 2
    Post what you have tried to do. – Andrey Nasonov Sep 29 '15 at 20:47
  • What about data binding? Use local settings and bind the picture box to it, and use the INotifyPropertyChanged interface – Matthias Herrmann Sep 29 '15 at 20:57
  • You know you can't update the UI from a background thread right? You'll need to get the UI thread to update UI stuff - SO contains Q/A on how to do that. Then on how to get the background stuff restarted, one way could be to have a timer and supply your background function as the callback; the timer is started which kicks off the callback; the callback stops the timer, does it stuff, calls the UI thread's dispatcher to update the UI, then starts the timer again which starts the whole thing over again. – 404 Sep 29 '15 at 21:30
  • @eurotrash, that would create a race condition. My FastPixel class encapsulation of Bitmap.LockBits() will potentially want to lock the bits again before the Image = m_fp.Bitmap assignment finishes. – Mike Reynolds Sep 29 '15 at 21:58
  • So I added all my present code, which uses the WorkerCompleted event to do the assignment on the UI thread. If I try to call RunAsync in this event it hits the race condition and I get a big red X. I don't know how to determine when the pictureBox1.Image assignment is completed. Delegates didn't work because they have to be static functions and I don't think I should be making pictureBox1 static in the Designer code. – Mike Reynolds Sep 29 '15 at 22:01
  • How about having the BackgroundWorker do all the work (not just one loop) and using the ProgressChanged event to change the image? That event handler runs on the UI thread. If you want to further modify the bitmap, though, you'll have to create a copy for display purposes. – vesan Sep 29 '15 at 22:21
  • So I just report 100% progress each time I want to call the assignment? I'll test that out right now! Thank you! – Mike Reynolds Sep 29 '15 at 22:29
  • No. That triggers a race condition again and I get a big red X. – Mike Reynolds Sep 29 '15 at 22:38
  • @MatthiasHermann, how do I bind a Bitmap or a Bitmap wrapper to a pictureBox? INotifyPropertyChange I think I can figure out, but not the binding part. – Mike Reynolds Sep 29 '15 at 22:59
  • Use `Invoke` method to update the Bitmap in the pictureBox. – Andrey Nasonov Sep 29 '15 at 23:02
  • Now your talkin'! But what do I use as the base object for the Invoke call? I did a search in the Object Browser and there are 1000+ hits on Invoke. – Mike Reynolds Sep 29 '15 at 23:14
  • I tried the following in the worker thread, but still got a big red X, and an ObjectDisposed exception on Form1: this.Invoke((MethodInvoker)delegate { pictureBox1.Image = m_fp.Bitmap; }); – Mike Reynolds Sep 29 '15 at 23:32
  • And thanks to the 2 human turds that voted my question down. Sure, this question is asked almost everyday, BECAUE NOBODY HAS AN ANSWER in ANY of these questions. Thank you so much for your thoughtful consideration of the problem. – Mike Reynolds Sep 30 '15 at 14:13
  • So Eurotrash gets the prize after all. Its a hacked kludge, but the ONE AND ONLY functional solution that anyone has suggested. – Mike Reynolds Sep 30 '15 at 14:16

2 Answers2

0

The Invoke method does work if I immediately Thread.Sleep() for 200ms.

            m_fp.Unlock();
            this.Invoke((MethodInvoker)delegate () { pictureBox1.Image = m_fp.Bitmap; });
            Thread.Sleep(200);
        } while (true);

Haven't tested the lower limit, but it won't be consistent across machines, so reporting it won't be informative.

-1

Open a new thread and create a while in of the function

Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);

workerThread.Start();

public void DoWork()
{
    while (!_shouldStop)
    {
        //REFRESH YOUR IMAGE
    }
}
  • 1
    @blackghost The mike you are talking to is not the OP. – Joakim Hansson Sep 29 '15 at 21:42
  • Wow! I wasn't expecting such a rapid response!! – Mike Reynolds Sep 29 '15 at 22:04
  • Why do I have a -2 next to my question? Isn't this a common issue in all sorts of computationally intensive graphics applications? One which after 10 hours of searching the web I could find no answer to, and I almost never fail to find some kind of code example I'm looking for on the web. – Mike Reynolds Sep 29 '15 at 22:44