private static void ShrinkExpandWPF(Profile profile, int width, int height, string sourceMapName, string targetMapName, double factor) { profile.Push("ShrinkExpand [WPF]"); using (SmartBitmap source = new SmartBitmap(new ManagedBitmap32(width, height, sourceMapName))) { profile.Push("Convert to WPF"); BitmapFrame wpfSource = source.AsWPF(); profile.Pop(); BitmapFrame wpfShrunkExpanded = Transforms.ShrinkExpand(profile, wpfSource, factor); profile.Push("Convert to Managed"); using (ManagedBitmap target = new ManagedBitmap32(wpfShrunkExpanded, targetMapName)) // copy into shared memory { } profile.Pop(); } profile.Pop(); }
// 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()); }