Пример #1
0
        private static bool TryGetTimestamps(string path, out DateTime fsCreated, out DateTime fsModified, out DateTime exifCreated)
        {
            exifCreated = default(DateTime);
            fsCreated   = File.GetCreationTime(path);
            fsModified  = File.GetLastWriteTime(path);

            return(ImageClient.QueryTimestampProperty(new Profile(), path, out exifCreated));
        }
Пример #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());
        }
Пример #3
0
        public static void Main(string[] args)
        {
            try
            {
                if ((args.Length >= 1) && String.Equals(args[0], "-waitdebugger"))
                {
                    while (!Debugger.IsAttached)
                    {
                        Thread.Sleep(250);
                    }
                    ShiftArgs(ref args, 1);
                }

                if ((args.Length == 2) && String.Equals(args[0], "-server"))
                {
                    Environment.ExitCode = ImageServer.RunServer(args[1]);
                    return;
                }


                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);


                bool showLogOnStart = false;
                while (args.Length >= 1)
                {
                    switch (args[0])
                    {
                    default:
                        goto Break;

                    case "-profile":
                        ProfileMode = true;
                        ShiftArgs(ref args, 1);
                        break;

                    case "-showlog":
                        showLogOnStart = true;
                        ShiftArgs(ref args, 1);
                        break;

                    case "-swap":
                        EnableSwap = true;
                        ShiftArgs(ref args, 1);
                        break;

                    case "-noswap":
                        EnableSwap = false;
                        ShiftArgs(ref args, 1);
                        break;
                    }
                }
Break:

                if (args.Length == 0)
                {
                    // HACK: based on what WinMerge does - usability is suspect but still much better than FolderBrowserDialog.
                    // See: http://www.codeproject.com/Articles/44914/Select-file-or-folder-from-the-same-dialog
                    using (OpenFileDialog dialog = new OpenFileDialog())
                    {
                        dialog.ValidateNames   = false;
                        dialog.CheckFileExists = false;
                        dialog.CheckPathExists = false;
                        dialog.Title           = "Select a Folder";
                        dialog.FileName        = "Select Folder.";
                        if (dialog.ShowDialog() != DialogResult.OK)
                        {
                            return;
                        }
                        args = new string[] { Path.GetDirectoryName(dialog.FileName) };
                    }
                }

                if (args.Length != 1)
                {
                    MessageBox.Show("Program must take one argument which is the path to a directory containing images to adjust.");
                    return;
                }

                Window     window;
                ImageCache cache = new ImageCache();

                string directory     = Path.GetFullPath(args[0]);
                string scanDirectory = GetScanDirectoryFromTargetDirectory(directory);

                if (!Directory.Exists(directory))
                {
                    MessageBox.Show(String.Format("Specified directory does not exist: \"{0}\"", directory));
                    return;
                }

                XmlDocument settings = new XmlDocument();
                {
                    string settingsPath = Path.Combine(scanDirectory, SettingsFile);
                    if (File.Exists(settingsPath))
                    {
                        settings.Load(settingsPath);
                    }
                }

                GlobalOptions options = new GlobalOptions(settings.CreateNavigator().SelectSingleNode("/*/options"));
                {
                    using (GlobalOptionsDialog dialog = new GlobalOptionsDialog(options, directory))
                    {
                        Application.Run(dialog);
                        if (dialog.DialogResult != DialogResult.OK)
                        {
                            return;
                        }
                    }
                }

                if (showLogOnStart)
                {
                    logWindow = new LoggingWindow();
                    logWindow.Show();
                }

                // scan items
                List <Item> items = new List <Item>();
                foreach (string filePath in Directory.GetFiles(scanDirectory))
                {
                    string fileName = Path.GetFileName(filePath);
                    if (String.Equals(fileName, SettingsFile, StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }

                    bool extUpper = String.Equals(Path.GetExtension(fileName), Path.GetExtension(fileName).ToUpper());
                    //fileName = Path.ChangeExtension(fileName, extUpper ? ".JPG" : ".jpg");

                    Item item = new Item(Path.Combine(directory, fileName), options, cache);
                    items.Add(item);

                    XPathNavigator itemNav = settings.CreateNavigator().SelectSingleNode(String.Format("/*/items/item[file=\"{0}\"]", fileName));
                    if (itemNav != null)
                    {
                        item.ReadXml(itemNav);
                    }
                }

                window = new Window(directory, items, cache, options);
                window.Show();

                window.LastAnalysisTask = BatchAnalyzerQueue.BeginAnalyzeBatch(items);


                Application.Run(window);


                LoggingWindow logw = logWindow;
                if (logw != null)
                {
                    logw.Close();
                }

                window.LastAnalysisTask.Wait(); // allow any unfinished actions to cancel and dispose state

                cache.Dispose();
                SerializationManager.Manager.Dispose();
                ImageClient.Close();
            }
            catch (Exception exception)
            {
                Debugger.Log(0, null, exception.ToString());
                MessageBox.Show(exception.ToString());
            }
        }
Пример #4
0
        public static void Main(string[] args)
        {
            try
            {
                if ((args.Length >= 1) && String.Equals(args[0], "-waitdebugger"))
                {
                    while (!Debugger.IsAttached)
                    {
                        Thread.Sleep(250);
                    }
                    ShiftArgs(ref args, 1);
                }

                if ((args.Length == 2) && String.Equals(args[0], "-server"))
                {
                    Environment.ExitCode = ImageServer.RunServer(args[1]);
                    return;
                }


                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);


                bool showLogOnStart = false;
                while (args.Length >= 1)
                {
                    switch (args[0])
                    {
                    default:
                        goto Break;

                    case "-profile":
                        ProfileMode = true;
                        ShiftArgs(ref args, 1);
                        break;

                    case "-showlog":
                        showLogOnStart = true;
                        ShiftArgs(ref args, 1);
                        break;

                    case "-swap":
                        EnableSwap = true;
                        ShiftArgs(ref args, 1);
                        break;

                    case "-noswap":
                        EnableSwap = false;
                        ShiftArgs(ref args, 1);
                        break;
                    }
                }
Break:

                if (args.Length == 0)
                {
                    // HACK: based on what WinMerge does - usability is suspect but still much better than FolderBrowserDialog.
                    // See: http://www.codeproject.com/Articles/44914/Select-file-or-folder-from-the-same-dialog
                    using (OpenFileDialog dialog = new OpenFileDialog())
                    {
                        dialog.ValidateNames   = false;
                        dialog.CheckFileExists = false;
                        dialog.CheckPathExists = false;
                        dialog.Title           = "Select a Folder";
                        dialog.FileName        = "Select Folder.";
                        if (dialog.ShowDialog() != DialogResult.OK)
                        {
                            return;
                        }
                        args = new string[] { Path.GetDirectoryName(dialog.FileName) };
                    }
                }

                if (args.Length != 1)
                {
                    MessageBox.Show("Program must take one argument which is the path to a directory containing images to adjust.");
                    return;
                }

                Window     window = null;
                ImageCache cache  = new ImageCache();

                string directory     = Path.GetFullPath(args[0]);
                string scanDirectory = GetScanDirectoryFromTargetDirectory(directory);

                if (!Directory.Exists(directory))
                {
                    MessageBox.Show(String.Format("Specified directory does not exist: \"{0}\"", directory));
                    return;
                }

                // load xml file
                XPathNavigator settingsNav;
                {
                    XmlDocument settings     = new XmlDocument();
                    string      settingsPath = Path.Combine(scanDirectory, SettingsFile);
                    if (File.Exists(settingsPath))
                    {
                        settings.Load(settingsPath);
                    }
                    settingsNav = settings.CreateNavigator();
                }

                GlobalOptions options = new GlobalOptions(settingsNav.SelectSingleNode("/*/options"));
                {
                    using (GlobalOptionsDialog dialog = new GlobalOptionsDialog(options, directory))
                    {
                        Application.Run(dialog);
                        if (dialog.DialogResult != DialogResult.OK)
                        {
                            return;
                        }
                    }
                }

                if (showLogOnStart)
                {
                    logWindow = new LoggingWindow();
                    logWindow.Show();
                }

                // read saved items from xml
                List <Item> items = new List <Item>();
                Dictionary <string, bool> filesWithItems = new Dictionary <string, bool>();
                foreach (XPathNavigator itemNav in settingsNav.Select("/*/items/item"))
                {
                    string fileName = itemNav.SelectSingleNode("file").Value;
                    string filePath = Path.Combine(directory, fileName);
                    if (File.Exists(filePath)) // drop item records for which file no longer exists
                    {
                        Item item = new Item(Path.Combine(directory, fileName), options, cache);
                        item.ReadXml(itemNav);

                        items.Add(item);

                        filesWithItems[fileName.ToLowerInvariant()] = false;
                    }
                }

                // add new records for any files that don't have a record
                foreach (string filePath in Directory.GetFiles(scanDirectory))
                {
                    string fileName = Path.GetFileName(filePath);
                    if (String.Equals(fileName, SettingsFile, StringComparison.OrdinalIgnoreCase))
                    {
                        continue;
                    }

                    if (!filesWithItems.ContainsKey(fileName.ToLowerInvariant()))
                    {
                        Item item = new Item(Path.Combine(directory, fileName), options, cache);
                        items.Add(item);

                        item.SettingsNav = settingsNav; // no match; try after hash has been computed
                    }
                }

                // timestamp actions
                if (options.Timestamps.HasValue)
                {
                    Parallel.ForEach(
                        items,
                        GetProcessorConstrainedParallelOptions2(CancellationToken.None),
                        delegate(Item item)
                    {
                        if (options.Timestamps.Value)
                        {
                            if (!options.TimestampsOverwriteExisting)
                            {
                                DateTime existingTimestamp;
                                if (TryParseFilenameTimestamp(Path.GetFileName(item.TargetPath), out existingTimestamp))
                                {
                                    return;
                                }
                            }

                            DateTime fsCreated, fsModified, exifCreated;
                            bool found = TryGetTimestamps(
                                item.SourcePath,
                                out fsCreated,
                                out fsModified,
                                out exifCreated);

                            string created  = fsCreated.ToString("s").Replace("T", ".");
                            string modified = fsModified.ToString("s").Replace("T", ".");
                            string exif     = found ? exifCreated.ToString("s").Replace("T", ".") : "<no data>";

                            string newName = StripFilenameTimestamp(Path.GetFileName(item.TargetPath));
                            string text    = FormatFilenameTimestamp(
                                found
                                        ? exifCreated
                                        : (!options.TimestampsExifMissingModifiedInsteadOfCreated ? fsCreated : fsModified));
                            newName = String.Concat(text, " ", newName);

                            item.RenamedFileName = newName;
                        }
                        else
                        {
                            item.RenamedFileName = StripFilenameTimestamp(Path.GetFileName(item.TargetPath));
                        }
                    });

                    // ensure no duplicate names
                    Dictionary <string, bool> usedName = new Dictionary <string, bool>();
                    foreach (Item item in items)
                    {
                        if (usedName.ContainsKey(item.RenamedFileName))
                        {
                            int suffix = 0;
                            while (true)
                            {
                                string newName = String.Concat(Path.GetFileNameWithoutExtension(item.RenamedFileName), "-", ++suffix, Path.GetExtension(item.RenamedFileName));
                                if (!usedName.ContainsKey(newName))
                                {
                                    item.RenamedFileName = newName;
                                    break;
                                }
                            }
                        }
                        usedName.Add(item.RenamedFileName, false);
                    }
                }

                if (items.Count != 0)
                {
                    window = new Window(directory, items, cache, options);
                    window.Show();

                    window.LastAnalysisTask = BatchAnalyzerQueue.BeginAnalyzeBatch(items);

                    Application.Run(window);
                }
                else
                {
                    MessageBox.Show(String.Format("The specified folder \"{0}\" contains no images.", scanDirectory));
                }

                LoggingWindow logw = logWindow;
                if (logw != null)
                {
                    logw.Close();
                }

                if (window != null)
                {
                    window.LastAnalysisTask.Wait(); // allow any unfinished actions to cancel and dispose state
                }

                cache.Dispose();
                SerializationManager.Manager.Dispose();
                ImageClient.Close();
            }
            catch (Exception exception)
            {
                Debugger.Log(0, null, exception.ToString());
                MessageBox.Show(exception.ToString());
            }
        }