Exemple #1
0
        public void Add(Profile copyFrom)
        {
            copyFrom.End();

            Stack <List <Entry> > stack = new Stack <List <Entry> >();

            stack.Push(new List <Entry>(new Entry[] { copyFrom.top }));
            parents.Push(current);
            while (stack.Count != 0)
            {
                List <Entry> list = stack.Pop();
                if (list.Count == 0)
                {
                    current = parents.Pop();
                    continue;
                }
                Entry item = list[0];
                list.RemoveAt(0);
                stack.Push(list);

                Entry level = new Entry(item.label, item.cumulative);
                current.subs.Add(level);
                parents.Push(current);
                current = level;

                stack.Push(new List <Entry>(item.subs));
            }
        }
Exemple #2
0
        // use highest quality transforms and no cached bitmaps
        public static void FinalOutputOne(Item item, Stats stats, Dictionary <string, bool> createdFiles, List <string> messages, CancellationTokenSource cancel)
        {
            Profile profile = new Profile("FinalOutput {0}", item.RenamedFileName);

            profile.Push("Item.WaitInit");
            item.WaitInit();
            profile.Pop();

            DateTime originalCreationTime  = File.GetCreationTime(item.SourcePath);
            DateTime originalLastWriteTime = File.GetLastWriteTime(item.SourcePath);

            string renamedTargetPath = Path.Combine(Path.GetDirectoryName(item.TargetPath), item.RenamedFileName);

            if (item.Delete)
            {
                // actual deletion occurs after end of run, by file being omitted from createdFiles
                Interlocked.Increment(ref stats.deleted);
            }
            else if (item.Valid)
            {
                // load source

                string tempFile = Path.GetTempFileName();
                File.Copy(item.SourcePath, tempFile, true /*overwrite*/);


                SmartBitmap bitmap = null;

                bool jpegTranRotationRequired = true;


                // initial lossy transforms

                if (item.NormalizeGeometry || (item.FineRotateDegrees != 0))
                {
                    if (bitmap == null)
                    {
                        bitmap = new SmartBitmap(Transforms.LoadAndOrientGDI(tempFile, profile));
                    }

                    jpegTranRotationRequired = false;
                    if (item.RightRotations != 0)
                    {
                        ManagedBitmap bitmap2 = bitmap.AsManaged().RotateFlip(Transforms.RotateFlipFromRightRotations(item.RightRotations));
                        bitmap.Dispose();
                        bitmap = new SmartBitmap(bitmap2);
                    }

                    Transforms.ApplyNormalizeGeometry(
                        profile,
                        item.SourceFileName,
                        bitmap.AsManaged(),
                        1,
                        new Transforms.NormalizeGeometryParameters(
                            item.CornerTL,
                            item.CornerTR,
                            item.CornerBL,
                            item.CornerBR,
                            item.NormalizeGeometryForcedAspectRatio,
                            item.FineRotateDegrees,
                            item.NormalizeGeometryFinalInterp),
                        delegate(string text) { },
                        cancel);
                }


                // lossless transform phase

                if (bitmap == null)
                {
                    List <string> jpegtranMessages = new List <string>();
                    string        error;

                    bool rotateOK = true;
                    if (jpegTranRotationRequired &&
                        ((item.RightRotations != 0) || (item.OriginalExifOrientation != RotateFlipType.RotateNoneFlipNone)))
                    {
                        rotateOK = false;
                        int combinedRotations = (item.RightRotations + Transforms.RightRotationsFromRotateFlipType(item.OriginalExifOrientation)) % 4;

                        // TODO: strip only Exif orientation after doing this - how? (currently strips all)
                        if (Transforms.LosslessRotateRightFinal(
                                tempFile,
                                tempFile,
                                combinedRotations,
                                out error))
                        {
                            Interlocked.Increment(ref stats.rotated);
                            rotateOK = true;
                        }
                        else
                        {
                            jpegtranMessages.Add(String.Format("Lossless rotate failed for \"{0}\" ({1}).", item.RenamedFileName, error));
                        }
                    }

                    bool cropOK = true;
                    if (!item.CropRect.IsEmpty)
                    {
                        cropOK = false;
                        if (Transforms.LosslessCropFinal(tempFile, tempFile, item.CropRect, out error))
                        {
                            Interlocked.Increment(ref stats.cropped);
                            cropOK = true;
                        }
                        else
                        {
                            jpegtranMessages.Add(String.Format("Lossless crop failed for \"{0}\" ({1}).", item.RenamedFileName, error));
                        }
                    }

                    if (!rotateOK || !cropOK)
                    {
                        // If jpegtran is unable to handle image (e.g. dimensions not integral multiples - as generated by
                        // some cameras), fall back to lossy rotation and log a message.

                        bitmap = new SmartBitmap(Transforms.LoadAndOrientGDI(tempFile, profile));

                        if (!rotateOK)
                        {
                            ManagedBitmap bitmap2 = bitmap.AsManaged().RotateFlip(Transforms.RotateFlipFromRightRotations(item.RightRotations));
                            bitmap.Dispose();
                            bitmap = new SmartBitmap(bitmap2);
                        }

                        if (!cropOK)
                        {
                            ManagedBitmap bitmap2 = bitmap.AsManaged().Crop(item.CropRect);
                            bitmap.Dispose();
                            bitmap = new SmartBitmap(bitmap2);
                        }

                        jpegtranMessages.Add("Using lossy rotation/crop instead.");
                        lock (messages)
                        {
                            messages.Add(String.Join(" ", jpegtranMessages.ToArray()));
                        }
                    }
                }
                else
                {
                    // whoops- preceding transform prevents jpegtran from being used (recreating jpeg would be lossy)

                    if (!item.CropRect.IsEmpty)
                    {
                        ManagedBitmap bitmap2 = bitmap.AsManaged().Crop(item.CropRect);
                        bitmap.Dispose();
                        bitmap = new SmartBitmap(bitmap2);
                    }
                }


                // following lossy transforms phase

                if (item.Unbias)
                {
                    if (bitmap == null)
                    {
                        bitmap = new SmartBitmap(Transforms.LoadAndOrientGDI(tempFile, profile));
                    }

                    Transforms.PolyUnbiasDiagnostics unused;
                    Transforms.ApplyPolyUnbias(
                        profile,
                        item.RenamedFileName,
                        bitmap.AsManaged(profile),
                        Rectangle.Empty /*already cropped*/,
                        new Transforms.PolyUnbiasParameters(
                            item.UnbiasMaxDegree,
                            item.UnbiasMaxChisq,
                            item.UnbiasMaxS,
                            item.UnbiasMinV),
                        null,
                        out unused,
                        cancel);

                    Interlocked.Increment(ref stats.polyUnbias);
                }

                if (item.BrightAdjust)
                {
                    if (bitmap == null)
                    {
                        bitmap = new SmartBitmap(Transforms.LoadAndOrientGDI(tempFile, profile));
                    }

                    Transforms.ApplyBrightAdjust(
                        profile,
                        item.RenamedFileName,
                        bitmap.AsManaged(profile),
                        Rectangle.Empty /*already cropped*/,
                        new Transforms.BrightAdjustParameters(item.BrightAdjustMinClusterFrac, item.BrightAdjustWhiteCorrect),
                        null,
                        cancel);

                    Interlocked.Increment(ref stats.brightAdjust);
                }

                if (item.StaticSaturate)
                {
                    if (bitmap == null)
                    {
                        bitmap = new SmartBitmap(Transforms.LoadAndOrientGDI(tempFile, profile));
                    }

                    Transforms.ApplyStaticSaturation(
                        profile,
                        item.RenamedFileName,
                        bitmap.AsManaged(profile),
                        Rectangle.Empty /*already cropped*/,
                        new Transforms.StaticSaturateParameters(item.StaticSaturateWhiteThreshhold, item.StaticSaturateBlackThreshhold, item.StaticSaturateExponent),
                        cancel);
                }

                if (item.Shrink)
                {
                    if (bitmap == null)
                    {
                        bitmap = new SmartBitmap(Transforms.LoadAndOrientGDI(tempFile, profile));
                    }

                    //Bitmap shrunk = Transforms.Shrink(profile, bitmap.AsGDI(profile), item.ShrinkFactor);
                    int           newWidth  = (int)Math.Floor(bitmap.Width / item.ShrinkFactor);
                    int           newHeight = (int)Math.Floor(bitmap.Height / item.ShrinkFactor);
                    ManagedBitmap bitmap2   = ImageClient.ResizeGDI(profile, bitmap.AsManaged(profile), newWidth, newHeight);
                    bitmap.Dispose();
                    bitmap = new SmartBitmap(bitmap2);

                    Interlocked.Increment(ref stats.shrunk);
                }

                // TODO: eliminate shrink and OneBit's expand if both are configured

                if (item.OneBit)
                {
                    if (bitmap == null)
                    {
                        bitmap = new SmartBitmap(Transforms.LoadAndOrientGDI(tempFile, profile));
                    }

                    SmartBitmap bitmap2 = new SmartBitmap(
                        Transforms.ApplyOneBit(
                            profile,
                            item.RenamedFileName,
                            bitmap,
                            new Transforms.OneBitParameters(item.OneBitChannel, item.OneBitThreshhold, item.OneBitScaleUp),
                            cancel));
                    bitmap.Dispose();
                    bitmap = bitmap2;

                    Interlocked.Increment(ref stats.oneBit);
                }

                if (bitmap != null)
                {
                    Transforms.SaveImage(profile, bitmap, tempFile, item.JpegQuality, item.JpegUseGdi, item.OutputFormat);
                    bitmap.Dispose();
                }


                // write target

                string targetPath;
                bool   extUpper = String.Equals(Path.GetExtension(renamedTargetPath), Path.GetExtension(renamedTargetPath).ToUpper());
                switch (item.OutputFormat)
                {
                default:
                    Debug.Assert(false);
                    throw new ArgumentException();

                case OutputFormat.Jpeg:
                    targetPath = Path.ChangeExtension(renamedTargetPath, extUpper ? ".JPG" : ".jpg");
                    break;

                case OutputFormat.Bmp:
                    targetPath = Path.ChangeExtension(renamedTargetPath, extUpper ? ".BMP" : ".bmp");
                    break;

                case OutputFormat.Png:
                    targetPath = Path.ChangeExtension(renamedTargetPath, extUpper ? ".PNG" : ".png");
                    break;
                }
                for (int i = 0; i <= RetryCount; i++)
                {
                    try
                    {
                        File.Copy(tempFile, targetPath, true /*overwrite*/);
                        File.SetCreationTime(targetPath, originalCreationTime);
                        File.SetLastWriteTime(targetPath, DateTime.Now);
                    }
                    catch (IOException) when(i < RetryCount)
                    {
                        // HACK: If folder is open, Explorer may have file locked to refresh thumbnail
                        Thread.Sleep(SleepRetry);
                    }
                }

                lock (createdFiles)
                {
                    createdFiles.Add(Path.GetFileName(targetPath).ToLowerInvariant(), false);
                }

                File.Delete(tempFile);
            }
            else
            {
                lock (createdFiles)
                {
                    createdFiles.Add(Path.GetFileName(renamedTargetPath).ToLowerInvariant(), false);
                }

                if (!String.Equals(item.SourcePath, renamedTargetPath, StringComparison.OrdinalIgnoreCase))
                {
                    File.Copy(item.SourcePath, renamedTargetPath, true /*overwrite*/);
                }
            }

            profile.End();
            Program.Log(LogCat.Perf, profile.Report());
        }
Exemple #3
0
        public void StartSwapOut(ImageCache cache)
        {
            lock (this)
            {
                Debug.Assert(Program.EnableSwap);

                if (this.outTask != null)
                {
                    return;
                }
                if (this.holders.Count != 0)
                {
                    return;
                }

                Stopwatch elapsed = Stopwatch.StartNew();

#if true // TODO: remove hack
                EventWaitHandle swapOutDelay = new EventWaitHandle(false, EventResetMode.AutoReset);
#endif
                Task <bool> serializationTask = null;

                Task <ManagedBitmap> oldBitmap = this.bitmap;

                // 'this' is not locked at the time 'cache' is used below in the task delegates

                this.outTask = new Task <bool>(
                    delegate()
                {
                    Profile profile = new Profile("SwapOut {0}", this.id);

#if true // TODO: remove hack
                    profile.Push("Hack delay for swapins");
                    // HACK: wait a little to allow swapins to start before swapouts
                    EventWaitHandle localSwapOutDelay = Interlocked.Exchange(ref swapOutDelay, null);
                    if (localSwapOutDelay != null)     // race: swapin can grab and clear this before we get here
                    {
                        localSwapOutDelay.WaitOne(100);
                    }
                    profile.Pop();
                    //
#endif
                    profile.Push("WaitSwapOutGate");
                    cache.WaitSwapOutGate();     // defer to in-flight swapins
                    profile.Pop();

                    lock (this)
                    {
                        Debug.Assert(this.swapFilePath == null);
                        Debug.Assert(this.swapFileStream == null);
                        this.swapFilePath   = Path.GetTempFileName();
                        this.swapFileStream = new FileStream(this.swapFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.DeleteOnClose);
                    }

                    profile.Push("oldBitmap.Wait()");
                    oldBitmap.Wait();     // wait for an old in-flight creation to complete
                    profile.Pop();

                    profile.Push("Serialize");
                    serializationTask = new Task <bool>(
                        delegate()
                    {
                        SetIOPriority(this.swapFileStream, PRIORITY_HINT.IoPriorityHintLow);
                        ManagedBitmap.Serialize(this.swapFileStream, oldBitmap.Result);
                        SetIOPriority(this.swapFileStream, PRIORITY_HINT.IoPriorityHintNormal);
                        return(false);
                    });
                    SerializationManager.Manager.EnqueueWriteTask(serializationTask);
                    serializationTask.Wait();
                    profile.Pop();

                    profile.Push("Enqueue oldBitmap");
                    lock (this)
                    {
                        this.oldBitmaps.Add(oldBitmap.Result);     // remove current bitmap and enqueue for destruction upon zero refs
                    }
                    profile.Pop();

                    profile.Push("Epilog");
                    cache.Trace("swapout", this, elapsed);
                    cache.PurgeDisposeList();
                    profile.Pop();

                    profile.End();
                    //Program.Log(LogCat.Perf, profile.ToString());

                    return(false);
                });

                this.bitmap = new Task <ManagedBitmap>(
                    delegate()
                {
                    Profile profile = new Profile("SwapIn {0}", this.id);

                    profile.Push("outTask.Wait()");
#if true // TODO: remove hack
                    // HACK: release delay immediately if swapin is requested
                    EventWaitHandle localSwapOutDelay = Interlocked.Exchange(ref swapOutDelay, null);
                    if (localSwapOutDelay != null)
                    {
                        localSwapOutDelay.Set();
                    }
                    //
#endif
                    SerializationManager.Manager.Prioritize(serializationTask);
                    Debug.Assert(this.outTask != null);
                    this.outTask.Wait();     // ensure in-progress swapout finishes
                    profile.Pop();

                    profile.Push("cache.BeginSwapIn()");
                    cache.BeginSwapIn();
                    profile.Pop();
                    try
                    {
                        Stopwatch elapsed2 = Stopwatch.StartNew();

                        Debug.Assert(this.swapFilePath != null);
                        Debug.Assert(this.swapFileStream != null);

                        profile.Push("Deserialize");
                        ManagedBitmap bitmap            = null;
                        Task <bool> deserializationTask = new Task <bool>(
                            delegate()
                        {
                            bitmap = ManagedBitmap.Deserialize(this.swapFileStream);
                            return(false);
                        });
                        SerializationManager.Manager.EnqueueReadTask(deserializationTask);
                        deserializationTask.Wait();
                        this.swapFilePath          = null;
                        Stream localSwapFileStream = this.swapFileStream;
                        this.swapFileStream        = null;
                        localSwapFileStream.Dispose();
                        this.outTask = null;
                        profile.Pop();

                        profile.Push("Epilog");
                        cache.Trace("swapin", this, elapsed2);
                        cache.PurgeDisposeList();

                        StartBitmapCompleteWaiter();

                        return(bitmap);
                    }
                    finally
                    {
                        cache.EndSwapIn();
#if true // TODO: remove hack
                        if (localSwapOutDelay != null)
                        {
                            localSwapOutDelay.Dispose();
                        }
#endif
                        profile.Pop();     // Epilog - here to include cache.EndSwapIn()

                        profile.End();
                        //Program.Log(LogCat.Perf, profile.ToString());
                    }
                });

                this.outTask.Start();
            }
        }
Exemple #4
0
        private const bool Logging = true; // TODO: turn off

        public static int RunServer(string pipeName)
        {
            TextWriter log;

            if (Logging)
            {
                int i = 0;
                while (true)
                {
                    try
                    {
                        log = new StreamWriter(Path.Combine(Path.GetTempPath(), String.Concat("ImageServerLog-", i, ".log")));
                    }
                    catch (Exception)
                    {
                        i++;
                        continue;
                    }
                    break;
                }
                log = TextWriter.Synchronized(log);
            }

            ManagedBitmap currentBitmap = null;

            try
            {
                ThreadPriority savedThreadPriority = Thread.CurrentThread.Priority;

                if (log != null)
                {
                    log.WriteLine("Connecting to pipe {0}", pipeName);
                    log.Flush();
                }
                using (NamedPipeClientStream channel = new NamedPipeClientStream(".", pipeName))
                {
                    channel.Connect();

                    Thread disconnectMonitorThread = new Thread(new ThreadStart(
                                                                    delegate()
                    {
                        TimeoutThread(channel);
                        if (log != null)
                        {
                            log.WriteLine("TimeoutThread terminated - connection broken, exiting");
                            log.Flush();
                        }
                    }));
                    disconnectMonitorThread.Start();

                    while (true)
                    {
                        if (log != null)
                        {
                            log.WriteLine("Waiting");
                            log.Flush();
                        }

                        ClientServerCommunication.Commands command;
                        object[] args;
                        ClientServerCommunication.ReceiveMessage(log, channel, out command, out args);

                        object[] extraResults = null;

                        if (log != null)
                        {
                            log.WriteLine("Processing command");
                            log.Flush();
                        }
                        Profile profile;
                        try
                        {
                            profile = new Profile("ImageServer process command");

                            if (log != null)
                            {
                                log.WriteLine("Command: {0}", command);
                                log.Flush();
                            }
                            switch (command)
                            {
                            default:
                                Debug.Assert(false);
                                throw new ArgumentException(command.ToString());

                            case ClientServerCommunication.Commands.Quit:
                                if (args.Length != 0)
                                {
                                    const string Message = "remote command: wrong number of arguments";
                                    Debug.Assert(false, Message);
                                    throw new ArgumentException(Message);
                                }
                                if (log != null)
                                {
                                    log.WriteLine("Main thread exiting");
                                    log.Flush();
                                    log.Close();
                                    log = null;
                                }
                                return(0);

                            case ClientServerCommunication.Commands.Clear:
                                if (args.Length != 0)
                                {
                                    const string Message = "remote command: wrong number of arguments";
                                    Debug.Assert(false, Message);
                                    throw new ArgumentException(Message);
                                }
                                if (currentBitmap == null)
                                {
                                    const string Message = "Current bitmap is null";
                                    Debug.Assert(false, Message);
                                    throw new InvalidOperationException(Message);
                                }
                                currentBitmap.Dispose();
                                currentBitmap = null;
                                break;

                            case ClientServerCommunication.Commands.LoadAndOrientGDI:
                                if (args.Length != 3)
                                {
                                    const string Message = "remote command: wrong number of arguments";
                                    Debug.Assert(false, Message);
                                    throw new ArgumentException(Message);
                                }
                                if (currentBitmap != null)
                                {
                                    const string Message = "Current bitmap hasn't been cleared";
                                    Debug.Assert(false, Message);
                                    throw new InvalidOperationException(Message);
                                }
                                Thread.CurrentThread.Priority = (ThreadPriority)args[0];
                                RotateFlipType exifOrientation;
                                LoadAndOrientGDI(
                                    profile,
                                    (string)args[1],
                                    (int)args[2],
                                    out currentBitmap,
                                    out exifOrientation);
                                extraResults = new object[] { (int)exifOrientation };
                                break;

                            case ClientServerCommunication.Commands.ResizeGDI:
                                if (args.Length != 6)
                                {
                                    const string Message = "remote command: wrong number of arguments";
                                    Debug.Assert(false, Message);
                                    throw new ArgumentException(Message);
                                }
                                if (currentBitmap != null)
                                {
                                    const string Message = "Current bitmap hasn't been cleared";
                                    Debug.Assert(false, Message);
                                    throw new InvalidOperationException(Message);
                                }
                                Thread.CurrentThread.Priority = (ThreadPriority)args[0];
                                ResizeGDI(
                                    profile,
                                    (string)args[1],
                                    (int)args[2],
                                    (int)args[3],
                                    (int)args[4],
                                    (int)args[5],
                                    out currentBitmap);
                                break;

                            case ClientServerCommunication.Commands.CreateTileGDI:
                                if (args.Length != 10)
                                {
                                    const string Message = "remote command: wrong number of arguments";
                                    Debug.Assert(false, Message);
                                    throw new ArgumentException(Message);
                                }
                                if (currentBitmap != null)
                                {
                                    const string Message = "Current bitmap hasn't been cleared";
                                    Debug.Assert(false, Message);
                                    throw new InvalidOperationException(Message);
                                }
                                Thread.CurrentThread.Priority = (ThreadPriority)args[0];
                                CreateTileGDI(
                                    profile,
                                    (string)args[1],
                                    (int)args[2],
                                    (int)args[3],
                                    (int)args[4],
                                    (int)args[5],
                                    (int)args[6],
                                    (int)args[7],
                                    (int)args[8],
                                    (int)args[9],
                                    out currentBitmap);
                                break;

                            case ClientServerCommunication.Commands.ShrinkExpandGDI:
                                if (args.Length != 6)
                                {
                                    const string Message = "remote command: wrong number of arguments";
                                    Debug.Assert(false, Message);
                                    throw new ArgumentException(Message);
                                }
                                Thread.CurrentThread.Priority = (ThreadPriority)args[0];
                                ShrinkExpandGDI(
                                    profile,
                                    (int)args[1],
                                    (int)args[2],
                                    (string)args[3],
                                    (string)args[4],
                                    (double)args[5]);
                                break;

                            case ClientServerCommunication.Commands.ShrinkExpandWPF:
                                if (args.Length != 6)
                                {
                                    const string Message = "remote command: wrong number of arguments";
                                    Debug.Assert(false, Message);
                                    throw new ArgumentException(Message);
                                }
                                Thread.CurrentThread.Priority = (ThreadPriority)args[0];
                                ShrinkExpandWPF(
                                    profile,
                                    (int)args[1],
                                    (int)args[2],
                                    (string)args[3],
                                    (string)args[4],
                                    (double)args[5]);
                                break;
                            }

                            profile.End();
                        }
                        catch (Exception exception)
                        {
                            if (log != null)
                            {
                                log.WriteLine("Recoverable exception: {0}", exception);
                                log.Flush();
                            }
                            ClientServerCommunication.SendMessage(
                                log,
                                channel,
                                ClientServerCommunication.Commands.Exception,
                                new object[] { exception.ToString() });
                            if (log != null)
                            {
                                log.WriteLine("Exception response sent");
                                log.Flush();
                            }
                            continue;
                        }
                        finally
                        {
                            Thread.CurrentThread.Priority = savedThreadPriority;
                        }

                        if (log != null)
                        {
                            log.WriteLine("Forming response");
                            log.Flush();
                        }
                        using (MemoryStream serializedProfileStream = new MemoryStream())
                        {
                            if (log != null)
                            {
                                log.WriteLine("Serializing profile");
                                log.Flush();
                            }
                            new BinaryFormatter().Serialize(serializedProfileStream, profile);

                            if (currentBitmap == null)
                            {
                                if (log != null)
                                {
                                    log.WriteLine("Sending 'Done'");
                                    log.Flush();
                                }
                                ClientServerCommunication.SendMessage(
                                    log,
                                    channel,
                                    ClientServerCommunication.Commands.Done,
                                    new object[] { serializedProfileStream.ToArray() });
                            }
                            else
                            {
                                if (log != null)
                                {
                                    log.WriteLine("Sending 'Pending'");
                                    log.Flush();
                                }
                                ClientServerCommunication.SendMessage(
                                    log,
                                    channel,
                                    ClientServerCommunication.Commands.Pending,
                                    new object[]
                                {
                                    (string)currentBitmap.BackingName,
                                    (int)currentBitmap.Width,
                                    (int)currentBitmap.Height,
                                    (byte[])serializedProfileStream.ToArray()
                                },
                                    extraResults);
                            }
                        }
                        if (log != null)
                        {
                            log.WriteLine("Sent");
                            log.Flush();
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                if (log != null)
                {
                    log.WriteLine("Unrecoverable exception: {0}", exception);
                    log.Flush();
                }
                if (currentBitmap != null)
                {
                    currentBitmap.Dispose();
                }
                if (!(exception is ClientServerCommunication.ChannelDisconnectedException))
                {
                    MessageBox.Show(exception.ToString());
                }
                if (log != null)
                {
                    log.WriteLine("Terminating abnormally");
                    log.Flush();
                    log.Close();
                    log = null;
                }
                return(1);
            }

#pragma warning disable CS0162 // unreachable
            if (log != null)
            {
                log.WriteLine("Terminating normally");
                log.Flush();
                log.Close();
                log = null;
            }
#pragma warning restore CS0162
            if (currentBitmap != null)
            {
                currentBitmap.Dispose();
            }
            return(0);
        }