<commit_before>﻿using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using System;
using System.Numerics;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Storage;

namespace Fotografix
{
    public sealed class PhotoEditor : IRenderScaleProvider, IDisposable
    {
        private readonly IPhoto photo;
        private readonly CanvasBitmap bitmap;
        private readonly Transform2DEffect histogramTransform;
        private PhotoAdjustment adjustment;
        private bool dirty;

        private PhotoEditor(IPhoto photo, CanvasBitmap bitmap)
        {
            this.photo = photo;
            this.bitmap = bitmap;
            this.histogramTransform = new Transform2DEffect { CacheOutput = true };
        }

        public void Dispose()
        {
            histogramTransform.Dispose();
            adjustment.Dispose();
            bitmap.Dispose();
        }

        public static Task<PhotoEditor> CreateAsync(IPhoto photo)
        {
            return CreateAsync(photo, CanvasDevice.GetSharedDevice());
        }

        public static async Task<PhotoEditor> CreateAsync(IPhoto photo, ICanvasResourceCreator canvasResourceCreator)
        {
            var bitmap = await photo.LoadBitmapAsync(canvasResourceCreator);
            var adjustment = await photo.LoadAdjustmentAsync();

            var editor = new PhotoEditor(photo, bitmap);
            editor.CanRevert = adjustment != null;
            editor.SetAdjustment(adjustment);
            return editor;
        }

        public int ThumbnailSize { get; set; } = 512;

        public IPhotoAdjustment Adjustment => adjustment;

        public bool AdjustmentEnabled
        {
            get => adjustment.Enabled;

            set
            {
                if (adjustment.Enabled != value)
                {
                    PreserveDirtyState(() => adjustment.Enabled = value);
                }
            }
        }

        public void Draw(CanvasDrawingSession ds)
        {
            ds.DrawImage(adjustment.Output);
        }

        public event EventHandler Invalidated;

        public Size OriginalSize => bitmap.Size;
        public Size RenderSize => adjustment.GetOutputSize(ResourceCreator);

        public float RenderScale
        {
            get => adjustment.RenderScale;

            set
            {
                if (adjustment.RenderScale != value)
                {
                    PreserveDirtyState(() => adjustment.RenderScale = value);
                }
            }
        }

        public void ScaleToFit(Size size)
        {
            Size photoSize = bitmap.Size;

            if (adjustment.Crop.HasValue)
            {
                photoSize = new Size(adjustment.Crop.Value.Width, adjustment.Crop.Value.Height);
            }

            this.RenderScale = (float)Math.Min(size.Width / photoSize.Width, size.Height / photoSize.Height);
        }

        public bool CanRevert { get; private set; }

        public async Task RevertAsync()
        {
            SetAdjustment(await photo.LoadAdjustmentAsync());
            this.dirty = false;
        }

        public void Reset()
        {
            SetAdjustment(new PhotoAdjustment());
            this.dirty = true;
        }

        public async Task SaveAsync()
        {
            if (dirty)
            {
                using (var thumbnail = await ExportToSoftwareBitmapAsync(ThumbnailSize))
                {
                    await photo.SaveAdjustmentAsync(adjustment, thumbnail);
                }

                this.dirty = false;
            }
        }

        public async Task<StorageFile> ExportAsync(StorageFolder folder)
        {
            using (var bitmap = await ExportToSoftwareBitmapAsync())
            {
                var file = await folder.CreateFileAsync(photo.Name + ".jpg", CreationCollisionOption.GenerateUniqueName);

                using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                {
                    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
                    encoder.SetSoftwareBitmap(bitmap);
                    await encoder.FlushAsync();
                }

                return file;
            }
        }

        public CanvasBitmap ExportToCanvasBitmap(int? maxDimension = null)
        {
            this.AdjustmentEnabled = true;

            var originalRenderScale = adjustment.RenderScale;
            adjustment.RenderScale = 1;

            var bounds = adjustment.Output.GetBounds(ResourceCreator);
            var scaledSize = ScaleDimensions(new Size(bounds.Width, bounds.Height), maxDimension);
            var rt = new CanvasRenderTarget(ResourceCreator, size: scaledSize);

            using (var ds = rt.CreateDrawingSession())
            {
                ds.DrawImage(adjustment.Output, rt.Bounds, bounds);
            }

            adjustment.RenderScale = originalRenderScale;
            return rt;
        }<commit_msg>Fix double file extension when exporting<commit_after>﻿using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Effects;
using System;
using System.IO;
using System.Numerics;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Storage;

namespace Fotografix
{
    public sealed class PhotoEditor : IRenderScaleProvider, IDisposable
    {
        private readonly IPhoto photo;
        private readonly CanvasBitmap bitmap;
        private readonly Transform2DEffect histogramTransform;
        private PhotoAdjustment adjustment;
        private bool dirty;

        private PhotoEditor(IPhoto photo, CanvasBitmap bitmap)
        {
            this.photo = photo;
            this.bitmap = bitmap;
            this.histogramTransform = new Transform2DEffect { CacheOutput = true };
        }

        public void Dispose()
        {
            histogramTransform.Dispose();
            adjustment.Dispose();
            bitmap.Dispose();
        }

        public static Task<PhotoEditor> CreateAsync(IPhoto photo)
        {
            return CreateAsync(photo, CanvasDevice.GetSharedDevice());
        }

        public static async Task<PhotoEditor> CreateAsync(IPhoto photo, ICanvasResourceCreator canvasResourceCreator)
        {
            var bitmap = await photo.LoadBitmapAsync(canvasResourceCreator);
            var adjustment = await photo.LoadAdjustmentAsync();

            var editor = new PhotoEditor(photo, bitmap);
            editor.CanRevert = adjustment != null;
            editor.SetAdjustment(adjustment);
            return editor;
        }

        public int ThumbnailSize { get; set; } = 512;

        public IPhotoAdjustment Adjustment => adjustment;

        public bool AdjustmentEnabled
        {
            get => adjustment.Enabled;

            set
            {
                if (adjustment.Enabled != value)
                {
                    PreserveDirtyState(() => adjustment.Enabled = value);
                }
            }
        }

        public void Draw(CanvasDrawingSession ds)
        {
            ds.DrawImage(adjustment.Output);
        }

        public event EventHandler Invalidated;

        public Size OriginalSize => bitmap.Size;
        public Size RenderSize => adjustment.GetOutputSize(ResourceCreator);

        public float RenderScale
        {
            get => adjustment.RenderScale;

            set
            {
                if (adjustment.RenderScale != value)
                {
                    PreserveDirtyState(() => adjustment.RenderScale = value);
                }
            }
        }

        public void ScaleToFit(Size size)
        {
            Size photoSize = bitmap.Size;

            if (adjustment.Crop.HasValue)
            {
                photoSize = new Size(adjustment.Crop.Value.Width, adjustment.Crop.Value.Height);
            }

            this.RenderScale = (float)Math.Min(size.Width / photoSize.Width, size.Height / photoSize.Height);
        }

        public bool CanRevert { get; private set; }

        public async Task RevertAsync()
        {
            SetAdjustment(await photo.LoadAdjustmentAsync());
            this.dirty = false;
        }

        public void Reset()
        {
            SetAdjustment(new PhotoAdjustment());
            this.dirty = true;
        }

        public async Task SaveAsync()
        {
            if (dirty)
            {
                using (var thumbnail = await ExportToSoftwareBitmapAsync(ThumbnailSize))
                {
                    await photo.SaveAdjustmentAsync(adjustment, thumbnail);
                }

                this.dirty = false;
            }
        }

        public async Task<StorageFile> ExportAsync(StorageFolder folder)
        {
            using (var bitmap = await ExportToSoftwareBitmapAsync())
            {
                var file = await folder.CreateFileAsync(Path.GetFileNameWithoutExtension(photo.Name) + ".jpg", CreationCollisionOption.GenerateUniqueName);

                using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite))
                {
                    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
                    encoder.SetSoftwareBitmap(bitmap);
                    await encoder.FlushAsync();
                }

                return file;
            }
        }

        public CanvasBitmap ExportToCanvasBitmap(int? maxDimension = null)
        {
            this.AdjustmentEnabled = true;

            var originalRenderScale = adjustment.RenderScale;
            adjustment.RenderScale = 1;

            var bounds = adjustment.Output.GetBounds(ResourceCreator);
            var scaledSize = ScaleDimensions(new Size(bounds.Width, bounds.Height), maxDimension);
            var rt = new CanvasRenderTarget(ResourceCreator, size: scaledSize);

            using (var ds = rt.CreateDrawingSession())
            {
                ds.DrawImage(adjustment.Output, rt.Bounds, bounds);
            }

            adjustment.RenderScale = originalRenderScale;
            return rt;
        }
{'fraction_non_alphanumeric': 0.07610186092066601, 'fraction_numerical': 0.0011753183153770813, 'mean_word_length': 1.7463690155997849, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6789790', 'n_tokens_mistral': 2551, 'n_tokens_neox': 2381, 'n_words': 551}