00001 using ZBar;
00002 using System;
00003 using Gtk;
00004 using Gdk;
00005 using System.Threading;
00006 using System.Media;
00007 using System.Collections.Generic;
00008 using System.IO;
00009 using System.Diagnostics;
00010
00011 namespace Foodolini.Activities.FoodRegistration
00012 {
00019 [System.ComponentModel.ToolboxItem(true)]
00020 public class Scanner : Gtk.DrawingArea
00021 {
00022 public Scanner(){
00023 this.Destroyed += HandleDestroyed;
00024 }
00025
00026 void HandleDestroyed(object sender, EventArgs e){
00027
00028 if(this.worker != null){
00029
00030 this.worker.Abort();
00031 this.worker.Join();
00032 }
00033 this.worker = null;
00034
00035
00036 if(this.overlay != null)
00037 this.overlay.Dispose();
00038 this.overlay = null;
00039 if(this.sourceMissing != null)
00040 this.sourceMissing.Dispose();
00041 this.sourceMissing = null;
00042 }
00043
00047 private string currentDevice = null;
00048
00055 public void Open(string device){
00056 if(this.worker != null)
00057 this.worker.Abort();
00058 this.worker = new Thread(this.ProcessVideo);
00059 this.currentDevice = device;
00060 this.worker.Start();
00061 }
00062
00063 public void Close(){
00064 if(this.worker != null){
00065 this.worker.Abort();
00066 this.worker.Join();
00067 lock(this.drawLock){
00068 this.toDraw = null;
00069 this.symbols = null;
00070 }
00071 this.QueueDraw();
00072 }
00073 this.worker = null;
00074 this.currentDevice = null;
00075 }
00076
00080 private System.Threading.Thread worker = null;
00081
00085 private System.Object drawLock = new System.Object();
00086
00091 private byte[] toDraw = null;
00092
00097 private int toDrawWidth;
00098
00103 private int toDrawHeight;
00104
00109 private List<Symbol> symbols = null;
00110
00114 private void ProcessVideo(){
00115 using(Video video = new Video()){
00116 try{
00117 video.Open(this.currentDevice);
00118 video.Enabled = true;
00119 using(ImageScanner scanner = new ImageScanner()){
00120 scanner.Cache = true;
00121 this.CaptureVideo(video, scanner);
00122 }
00123 video.Enabled = false;
00124 }
00125 catch(ZBarException ex){
00126 lock(this.drawLock){
00127 this.toDraw = null;
00128 this.symbols = null;
00129 }
00130 GLib.IdleHandler hdl = delegate(){
00131 if(this.Stopped != null)
00132 this.Stopped(this, new EventArgs());
00133 if(this.Error != null)
00134 this.Error(this, new ErrorEventArgs(ex.Message, ex));
00135 this.QueueDraw();
00136 return false;
00137 };
00138 GLib.Idle.Add(hdl);
00139 }
00140 }
00141 }
00142
00149 private void CaptureVideo(Video video, ImageScanner scanner){
00150 while(true){
00151 using(ZBar.Image frame = video.NextFrame()){
00152 using(ZBar.Image bwImg = frame.Convert(0x30303859)){
00153
00154 scanner.Scan(bwImg);
00155
00156 byte[] data = bwImg.Data;
00157 int w = (int)bwImg.Width;
00158 int h = (int)bwImg.Height;
00159 var symbols = new List<Symbol>(bwImg.Symbols);
00160
00161 if(this.Flip){
00162 for(int ih = 0; ih < h; ih++){
00163 for(int iw = 0; iw < w / 2; iw++){
00164
00165 int p1 = w * ih + iw;
00166 int p2 = w * ih + (w - iw- 1);
00167
00168 byte b1 = data[p1];
00169 data[p1] = data[p2];
00170 data[p2] = b1;
00171 }
00172 }
00173 }
00174
00175 lock(this.drawLock){
00176 this.toDraw = data;
00177 this.toDrawWidth = w;
00178 this.toDrawHeight = h;
00179 this.symbols = symbols;
00180 }
00181 this.ThreadSafeRedraw();
00182 }
00183 }
00184 }
00185 }
00186
00190 private void ThreadSafeRedraw(){
00191 GLib.IdleHandler hdl = delegate(){
00192 this.QueueDraw();
00193 return false;
00194 };
00195 GLib.Idle.Add(hdl);
00196 }
00197
00201 public event EventHandler Stopped;
00202
00206 public event EventHandler<ErrorEventArgs> Error;
00207
00212 public event EventHandler<BarScannedArgs> BarScanned;
00213
00214 private string data = null;
00215 private const int overlayFrameCount = 35;
00216 private int overlayingFrames = 0;
00217 private Pixbuf overlay = Pixbuf.LoadFromResource("FoodRegistration.check.png");
00218 private Pixbuf sourceMissing = Pixbuf.LoadFromResource("FoodRegistration.webcam.png");
00219
00224 public void ResetLastItemScanned(){
00225 this.data = "";
00226 }
00227
00228 protected override bool OnExposeEvent(Gdk.EventExpose ev)
00229 {
00230 Gdk.Window win = ev.Window;
00231 Gdk.Rectangle rect = ev.Area;
00232 Gdk.GC gc = this.Style.BaseGC(StateType.Normal);
00233 lock(this.drawLock){
00234 if(this.toDraw != null){
00235
00236 bool gotSymbol = false;
00237
00238 if(this.symbols != null){
00239 foreach(Symbol s in this.symbols){
00240 if(s.Count > 0 && this.data != s.ToString()){
00241 this.data = s.ToString();
00242
00243 GLib.IdleHandler raiser = delegate(){
00244 if(this.BarScanned != null)
00245 this.BarScanned(this, new BarScannedArgs(s));
00246 return false;
00247 };
00248 GLib.Idle.Add(raiser);
00249 gotSymbol = true;
00250 }
00251 }
00252 }
00253
00254 if(gotSymbol){
00255 if(!this.Mute)
00256 System.Media.SystemSounds.Beep.Play();
00257 if(this.overlayingFrames == 0){
00258 GLib.TimeoutHandler hdl = delegate(){
00259 this.QueueDraw();
00260 this.overlayingFrames -= 1;
00261 return this.overlayingFrames > 0;
00262 };
00263 GLib.Timeout.Add(35, hdl);
00264 }
00265
00266 this.overlayingFrames = overlayFrameCount;
00267 }
00268 this.symbols = null;
00269
00270
00271 if(this.reqHeight != this.toDrawHeight ||
00272 this.reqWidth != this.toDrawWidth){
00273 this.reqHeight = this.toDrawHeight;
00274 this.reqWidth = this.toDrawWidth;
00275 this.QueueResize();
00276 }
00277
00278
00279 int w = Math.Min(rect.Width, this.toDrawWidth);
00280 int h = Math.Min(rect.Height, this.toDrawHeight);
00281
00282
00283 win.DrawGrayImage(gc, 0, 0, w, h, Gdk.RgbDither.Normal, this.toDraw, this.toDrawWidth);
00284
00285 if(this.overlayingFrames > 0){
00286 w = Math.Min(this.AllocatedWidth, (int)this.overlay.Width);
00287 h = Math.Min(this.AllocatedHeight, (int)this.overlay.Height);
00288 using(Gdk.Pixbuf pix = new Pixbuf(Colorspace.Rgb, true, 8, w, h)){
00289 pix.Fill(0x00000000);
00290 this.overlay.Composite(pix, 0, 0, w, h, 0, 0, 1, 1, InterpType.Bilinear, 255 / 35 * this.overlayingFrames);
00291 win.DrawPixbuf(gc, pix, 0, 0,
00292 (this.AllocatedWidth - w) / 2,
00293 (this.AllocatedHeight - h) / 2, w, h, RgbDither.Normal, 0, 0);
00294 }
00295 }
00296 }else{
00297 win.DrawRectangle(gc, true, rect);
00298
00299 int w = Math.Min(this.AllocatedWidth, (int)this.sourceMissing.Width);
00300 int h = Math.Min(this.AllocatedHeight, (int)this.sourceMissing.Height);
00301
00302 Rectangle img = new Rectangle((this.AllocatedWidth - w) / 2,
00303 (this.AllocatedHeight - h) / 2,
00304 w, h);
00305 Rectangle target = Rectangle.Intersect(img, rect);
00306 if(target != Rectangle.Zero){
00307 win.DrawPixbuf(gc, this.sourceMissing,
00308 Math.Max(target.X - img.X, 0),
00309 Math.Max(target.Y - img.Y, 0),
00310 target.X,
00311 target.Y,
00312 target.Width,
00313 target.Height,
00314 RgbDither.Normal, 0, 0);
00315 }
00316 }
00317 }
00318 return true;
00319 }
00320
00321 protected override void OnSizeAllocated(Gdk.Rectangle allocation){
00322 base.OnSizeAllocated(allocation);
00323 this.AllocatedWidth = allocation.Width;
00324 this.AllocatedHeight = allocation.Height;
00325 }
00326
00327 private int AllocatedWidth;
00328 private int AllocatedHeight;
00329
00330 private int reqHeight = 200;
00331 private int reqWidth = 200;
00332
00333 protected override void OnSizeRequested(ref Gtk.Requisition requisition){
00334
00335 requisition.Height = this.reqHeight;
00336 requisition.Width = this.reqWidth;
00337 }
00338
00342 public bool Mute{get; set;}
00343
00351 public bool Flip{get; set;}
00352
00373 [Obsolete("Don't use this method anymore, the classes in Foodolini.Activities.FoodRegistration.Devices can detect device as they are plugged.")]
00374 public static IDictionary<string, string> ListVideoSources(){
00375 var retval = new Dictionary<string, string>();
00376
00377 int p = (int) Environment.OSVersion.Platform;
00378 if((p == (int)PlatformID.Unix) || (p == (int)PlatformID.MacOSX) || (p == 128)) {
00379
00380 try{
00381
00382
00383
00384 #if UDEV_RULES
00385 try{
00386
00387
00388 foreach(var device in Directory.GetFiles("/dev/v4l/by-id/")){
00389 var name = Path.GetFileName(device);
00390 name = name.Remove(0, name.IndexOf("_") + 1);
00391 name = name.Remove(name.IndexOf("-video-index"));
00392 if(name == string.Empty)
00393 throw new Exception("Hack that jumps aways from here :) ");
00394 retval.Add(name, device);
00395 }
00396 }
00397 catch{
00398
00399 foreach(var device in Directory.GetFiles("/dev/", "video*")){
00400
00401 var name = "Video " + Path.GetFileName(device).Remove(0, 5);
00402 retval.Add(name, device);
00403 }
00404 }
00405 #else
00406 foreach(var device in Directory.GetFiles("/dev/", "video*")){
00407 string name = null;
00408
00409 bool supports_capture = true;
00410
00411
00412
00413
00414
00415 try{
00416 using(Process v4l_id = new Process()){
00417 v4l_id.StartInfo.UseShellExecute = false;
00418 v4l_id.StartInfo.FileName = "/lib/udev/v4l_id";
00419 v4l_id.StartInfo.CreateNoWindow = true;
00420 v4l_id.StartInfo.Arguments = device;
00421 v4l_id.StartInfo.RedirectStandardOutput = true;
00422 v4l_id.Start();
00423 string input;
00424 while((input = v4l_id.StandardOutput.ReadLine()) != null){
00425 if(input.StartsWith("ID_V4L_PRODUCT="))
00426 name = input.Remove(0, 15);
00427 if(input.StartsWith("ID_V4L_CAPABILITIES="))
00428 supports_capture = input.Contains(":capture:");
00429 }
00430 v4l_id.WaitForExit();
00431 if(v4l_id.ExitCode != 0)
00432 throw new Exception("Don't use anything we've got here...");
00433 }
00434 }
00435 catch(Exception e){
00436
00437 name = "Video " + System.IO.Path.GetFileName(device).Remove(0, 5);
00438 }
00439
00440 if(supports_capture)
00441 retval.Add(name, device);
00442 }
00443 #endif
00444 }
00445 catch{
00446
00447
00448
00449 retval.Add("Default video device", "/dev/video0");
00450 }
00451 }else{
00452
00453 retval.Add("Default video device", "/dev/video0");
00454
00455
00456
00457 }
00458 return retval;
00459 }
00460 }
00461
00465 public class ErrorEventArgs : System.EventArgs{
00469 public string Message{get; private set;}
00470
00474 public ZBarException Exception{get; private set;}
00475
00476 public ErrorEventArgs(string message){
00477 this.Message = message;
00478 }
00479
00480 public ErrorEventArgs(string message, ZBarException innerException) : this(message) {
00481 this.Exception = innerException;
00482 }
00483 }
00484 }