/// <summary> /// Generate the stereogram on a background thread. First wait for the specified interval and see if another request pre-empts us. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void generate_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; // Keep generating as long as there's a request in the pipe while (requestOptions != null) { Options options = requestOptions; requestOptions = null; if (options.time.Ticks > DateTime.Now.Ticks) { TimeSpan delta = new TimeSpan(options.time.Ticks - DateTime.Now.Ticks); if (delta.TotalMilliseconds > 50) { Thread.Sleep(delta); } } if (worker.CancellationPending) { e.Cancel = true; return; } if (options != null && requestOptions == null) { ThreadPriority old = Thread.CurrentThread.Priority; try { Thread.CurrentThread.Priority = ThreadPriority.BelowNormal; generator = StereogramGenerator.Get(options); Stereogram stereogram = generator.Generate(); stereogram.Name = String.Format("{0} + {1}", options.depthmap.Name, options.texture.Name); e.Result = stereogram; } finally { Thread.CurrentThread.Priority = old; } } } }
// When the thread completes, we hopefully have a completed stereogram to return to the callback private void generate_Completed(object sender, RunWorkerCompletedEventArgs e) { // First, handle the case where an exception was thrown. if (e.Error != null) { // Let the callback handle a NULL result if (callback != null) { callback(null); } } else if (e.Cancelled) { } else if (e.Result is Stereogram) { Stereogram stereogram = (Stereogram)e.Result; if (callback != null) { callback(stereogram); } } }
/// <summary> /// Generate the stereogram. Does all the common functionality, then calls the delegate /// set by the subclass to do the actual work. /// </summary> public Stereogram Generate() { // Let's do some profiling System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch(); timer.Start(); // Convert texture to RGB24 and scale it to fit the separation (preserving ratio but doubling width for HQ mode) bmTexture = texture.GetToScaleAndFormat(textureWidth, textureHeight, PixelFormats.Pbgra32); // Resize the depthmap to our target resolution bmDepthMap = depthmap.GetToScale(depthWidth, resolutionY); // Create a great big 2D array to hold the bytes - wasteful but convenient // ... and necessary for parallelisation pixels = new UInt32[lineWidth * rows]; // Copy the texture data into a buffer texturePixels = new UInt32[textureWidth * textureHeight]; bmTexture.CopyPixels(new Int32Rect(0, 0, textureWidth, textureHeight), texturePixels, textureWidth * bytesPerPixel, 0); // Copy the depthmap data into a buffer depthBytes = new byte[depthWidth * rows]; bmDepthMap.CopyPixels(new Int32Rect(0, 0, depthWidth, rows), depthBytes, depthWidth, 0); // Can mock up a progress indicator GeneratedLines = 0; // Prime candidate for Parallel.For... yes, about doubles the speed of generation on my Quad-Core if (System.Diagnostics.Debugger.IsAttached) // Don't run parallel when debugging { for (int y = 0; y < rows; y++) { DoLine(y); if (y > GeneratedLines) { GeneratedLines = y; } } } else { Parallel.For(0, rows, y => { if (false == abort) { DoLine(y); } if (y > GeneratedLines) { GeneratedLines = y; } }); } if (abort) { return(null); } // Virtual finaliser... not needed for any current algorithms Finalise(); // Create a writeable bitmap to dump the stereogram into wbStereogram = new WriteableBitmap(lineWidth, resolutionY, 96.0, 96.0, bmTexture.Format, bmTexture.Palette); wbStereogram.WritePixels(new Int32Rect(0, 0, lineWidth, rows), pixels, lineWidth * bytesPerPixel, 0); BitmapSource bmStereogram = wbStereogram; // High quality images need to be scaled back down... if (oversample > 1) { double over = (double)oversample; double centre = lineWidth / 2; while (over > 1) { // Scale by steps... could do it in one pass, but quality would depend on what the hardware does? double div = Math.Min(over, 2.0); // double div = over; ScaleTransform scale = new ScaleTransform(1.0 / div, 1.0, centre, 0); bmStereogram = new TransformedBitmap(bmStereogram, scale); over /= div; centre /= div; } } if (bAddConvergenceDots) { // Because I made these fields read-only, I can't now restore them... 'spose I could add the dots at hi-res but I'd still need to account for the stretching double sep = separation / oversample; double mid = midpoint / oversample; RenderTargetBitmap rtStereogram = new RenderTargetBitmap(bmStereogram.PixelWidth, bmStereogram.PixelHeight, 96.0, 96.0, PixelFormats.Pbgra32); DrawingVisual dots = new DrawingVisual(); DrawingContext dc = dots.RenderOpen(); dc.DrawImage(bmStereogram, new Rect(0.0, 0.0, rtStereogram.Width, rtStereogram.Height)); dc.DrawEllipse(new SolidColorBrush(Colors.Black), new Pen(new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)), 1.0), new Point(mid - sep / 2, rtStereogram.Height / 16), sep / 16, sep / 16); dc.DrawEllipse(new SolidColorBrush(Colors.Black), new Pen(new SolidColorBrush(Color.FromArgb(128, 0, 0, 0)), 1.0), new Point(mid + sep / 2, rtStereogram.Height / 16), sep / 16, sep / 16); dc.Close(); rtStereogram.Render(dots); bmStereogram = rtStereogram; } // Freeze the bitmap so it can be passed to other threads bmStereogram.Freeze(); timer.Stop(); Stereogram stereogram = new Stereogram(bmStereogram); stereogram.options = this.options; stereogram.Name = String.Format("{0}+{1}+{2}", depthmap.Name, texture.Name, options.algorithm.ToString()); stereogram.Milliseconds = timer.ElapsedMilliseconds; return(stereogram); }