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)); } }
// 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()); }
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(); } }
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); }