private static void makeFullImage(Photo.Metadata metadata, BitmapImage img)
 {
     if (DownsampleFullImage && (
             metadata.Width > SystemParameters.MaximizedPrimaryScreenWidth ||
             metadata.Height > SystemParameters.MaximizedPrimaryScreenHeight))
     {
         if (metadata.Width * SystemParameters.MaximizedPrimaryScreenHeight >
             metadata.Height * SystemParameters.MaximizedPrimaryScreenWidth)
         {
             img.DecodePixelWidth = (int)SystemParameters.MaximizedPrimaryScreenWidth;
         }
         else
         {
             img.DecodePixelHeight = (int)SystemParameters.MaximizedPrimaryScreenHeight;
         }
     }
     img.CacheOption = BitmapCacheOption.None;
     img.Rotation    = Exif.OrienationToRotation(metadata.Orientation);
     img.EndInit();
     img.Freeze();
 }
        private static async Task <Photo.Metadata> reloadAndSave(
            Photo photo, bool forceJpeg, string destFile)
        {
            using (var mmap = MemoryMappedFile.OpenExisting(
                       mmapName(photo.FileName),
                       MemoryMappedFileRights.Read)) {
                using (var stream = new UnsafeMemoryMapStream(
                           mmap.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read),
                           FileAccess.Read)) {
                    (var sourceFrame, var format) = loadFrame(stream.Stream);
                    var            md          = sourceFrame.Metadata.Clone() as BitmapMetadata;
                    Photo.Metadata newMetadata = await Exif.SetMetadata(photo, md);

                    newMetadata.Width  = sourceFrame.PixelWidth;
                    newMetadata.Height = sourceFrame.PixelHeight;
                    BitmapEncoder encoder = forceJpeg ?
                                            new JpegBitmapEncoder() :
                                            BitmapEncoder.Create(format);
                    encoder.Frames.Add(
                        BitmapFrame.Create(
                            sourceFrame,
                            null,
                            md,
                            sourceFrame.ColorContexts));
                    if (encoder is JpegBitmapEncoder jpg)
                    {
                        jpg.Rotation = Exif.SaveRotation(md);
                    }
                    sourceFrame = null;
                    using (var outFile = new FileStream(destFile, FileMode.CreateNew)) {
                        encoder.Save(outFile);
                    }
                    return(newMetadata);
                }
            }
        }
        /// <summary>
        /// Remove <paramref name="oldFile"/> after closing any handles
        /// <paramref name="photo"/> might have open for it and replacing them
        /// with new ones.
        /// </summary>
        private static async Task reloadAndRemove(Photo photo, Photo.Metadata metadata, string oldFile)
        {
            var oldMmap = await photo.Dispatcher.InvokeAsync(() => {
                if (photo.Disposed)
                {
                    return(mmap : null, stream : null);
                }
                return(mmap : Interlocked.Exchange(ref photo.mmap, null),
                       stream : Interlocked.Exchange(ref photo.fullImageStream, null));
            });

            if (oldMmap.mmap != null)
            {
                try {
                    if (oldMmap.stream != null)
                    {
                        oldMmap.stream.Dispose();
                    }
                } finally {
                    oldMmap.mmap.Dispose();
                }
                try {
                    var mmap = MemoryMappedFile.CreateFromFile(
                        File.Open(photo.FileName,
                                  FileMode.Open,
                                  FileAccess.Read,
                                  FileShare.Delete | FileShare.Read),
                        mmapName(photo.FileName),
                        0,
                        MemoryMappedFileAccess.Read,
                        HandleInheritability.None,
                        false);
                    if (await photo.Dispatcher.InvokeAsync(() => {
                        if (photo.Disposed)
                        {
                            return(false);
                        }
                        photo.mmap = mmap;
                        return(true);
                    }))
                    {
                        await setImage(photo, metadata, false);
                    }
                    else
                    {
                        mmap.Dispose();
                    }
                } catch (Exception ex) {
                    await photo.Dispatcher.InvokeAsync(() => {
                        MessageBox.Show(ex.ToString(),
                                        string.Format("Reloading {0}\n\n{1}",
                                                      photo.FileName, ex),
                                        MessageBoxButton.OK,
                                        MessageBoxImage.Error);
                    });
                }
            }
            try {
                File.Delete(oldFile);
            } catch (Exception ex) {
                await photo.Dispatcher.InvokeAsync(() => {
                    MessageBox.Show(ex.ToString(),
                                    string.Format("Error overwriting {0}\n\n{1}",
                                                  photo.FileName, ex),
                                    MessageBoxButton.OK,
                                    MessageBoxImage.Error);
                });
            }
        }
 internal void EnqueueFullSizeRead(Photo photo, Photo.Metadata meta)
 {
     fullsizeReads.Add(new Tuple <Photo, Photo.Metadata>(photo, meta));
     makeIOThread();
 }
        private async static Task setImage(Photo photo, Photo.Metadata metadata, bool mustLock)
        {
            bool locked = false;

            if (photo.Disposed)
            {
                return;
            }
            try {
                if (mustLock)
                {
                    // To avoid dispose colliding with setImage.
                    await photo.loadLock.WaitAsync();

                    locked = true;
                }
                if (photo.Disposed)
                {
                    return;
                }
                var(mmap, fullImageStream) = await photo.Dispatcher.InvokeAsync(
                    () => (photo.mmap, photo.fullImageStream));

                if (mmap == null)
                {
                    // disposed
                    return;
                }
                if (fullImageStream == null)
                {
                    fullImageStream = new UnsafeMemoryMapStream(
                        mmap.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read),
                        FileAccess.Read);
                    await photo.Dispatcher.InvokeAsync(() => {
                        var p = Interlocked.CompareExchange(
                            ref photo.fullImageStream, fullImageStream, null);
                        if (p != null)
                        {
                            fullImageStream.Dispose();
                            fullImageStream = p;
                        }
                    });
                }
                var img = new BitmapImage();
                try {
                    img.BeginInit();
                    img.StreamSource = fullImageStream.Stream;
                    makeFullImage(metadata, img);
                } catch (Exception ex) {
                    await photo.Dispatcher.InvokeAsync(() => {
                        MessageBox.Show(ex.ToString(),
                                        string.Format("Error loading {0}\n\n{1}",
                                                      photo.FileName, ex),
                                        MessageBoxButton.OK,
                                        MessageBoxImage.Error);
                    });

                    return;
                }
                await photo.Dispatcher.InvokeAsync(() => {
                    photo.FullImage = img;
                });
            } finally {
                if (locked)
                {
                    photo.loadLock.Release();
                }
            }
            // Now is a good time to do a gen-0 GC to make sure the image we
            // just loaded gets promoted to at least gen1 before its caching
            // expires.
            GC.Collect(0);
        }