From 63bee738b21fac3e23813e500467f09bca5d5082 Mon Sep 17 00:00:00 2001 From: Syping Date: Sun, 23 Nov 2025 05:45:55 +0100 Subject: [PATCH] impl. image-as-is option --- Commands.cs | 40 +++++++++++++++++++++++++++++++++------- Jpeg.cs | 51 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/Commands.cs b/Commands.cs index 52f2ed4..e68ccbd 100644 --- a/Commands.cs +++ b/Commands.cs @@ -5,7 +5,8 @@ namespace RagePhoto.Cli; internal static partial class Commands { - internal static Int32 CreateFunction(String format, String? imageFile, String? description, String? json, String? title, String? outputFile) { + internal static Int32 CreateFunction(String format, String? imageFile, bool imageAsIs, + String? description, String? json, String? title, String? outputFile) { try { using Photo photo = new(); @@ -28,7 +29,7 @@ internal static partial class Commands { } else { using Stream input = imageFile == "-" ? Console.OpenStandardInput() : File.OpenRead(imageFile); - photo.Jpeg = Jpeg.GetJpeg(input, out size); + photo.Jpeg = Jpeg.GetJpeg(input, imageAsIs, out size); } photo.Json = json == null ? @@ -147,7 +148,8 @@ internal static partial class Commands { } } - internal static Int32 SetFunction(String inputFile, String? format, String? imageFile, String? description, String? json, String? title, bool updateJson, String? outputFile) { + internal static Int32 SetFunction(String inputFile, String? format, String? imageFile, bool imageAsIs, + String? description, String? json, String? title, bool updateJson, String? outputFile) { try { if (format == null && imageFile == null && description == null && json == null && title == null && !updateJson) { @@ -193,7 +195,7 @@ internal static partial class Commands { } else if (imageFile != null) { using Stream input = imageFile == "-" ? Console.OpenStandardInput() : File.OpenRead(imageFile); - photo.Jpeg = Jpeg.GetJpeg(input, out size); + photo.Jpeg = Jpeg.GetJpeg(input, imageAsIs, out size); photo.Json = Json.Update(photo, size, photo.Json, out Int32 uid); } else if (updateJson) { @@ -243,6 +245,9 @@ internal static partial class Commands { Option imageOption = new("--image", "-i", "--jpeg") { Description = "Image File" }; + Option imageAsIsOption = new("--image-as-is") { + Description = "Image as-is" + }; Option descriptionOption = new("--description", "-d") { Description = "Description" }; @@ -256,11 +261,18 @@ internal static partial class Commands { Description = "Output File" }; Command createCommand = new("create", "Create a new Photo") { - formatArgument, imageOption, descriptionOption, jsonOption, titleOption, outputOption + formatArgument, + imageOption, + imageAsIsOption, + descriptionOption, + jsonOption, + titleOption, + outputOption }; createCommand.SetAction(result => Environment.ExitCode = CreateFunction( result.GetRequiredValue(formatArgument), result.GetValue(imageOption), + result.GetValue(imageAsIsOption), result.GetValue(descriptionOption), result.GetValue(jsonOption), result.GetValue(titleOption), @@ -302,7 +314,9 @@ internal static partial class Commands { DefaultValueFactory = _ => "-" }; Command getCommand = new("get", "Get Data from a Photo") { - inputArgument, typeArgument, outputOption + inputArgument, + typeArgument, + outputOption }; getCommand.SetAction(result => Environment.ExitCode = GetFunction( result.GetRequiredValue(inputArgument), @@ -323,6 +337,9 @@ internal static partial class Commands { Option imageOption = new("--image", "-i", "--jpeg") { Description = "Image File" }; + Option imageAsIsOption = new("--image-as-is") { + Description = "Image as-is" + }; Option descriptionOption = new("--description", "-d") { Description = "Description" }; @@ -339,12 +356,21 @@ internal static partial class Commands { Description = "Output File" }; Command setCommand = new("set", "Set Data from a Photo") { - inputArgument, formatOption, imageOption, descriptionOption, jsonOption, titleOption, updateJsonOption, outputOption + inputArgument, + formatOption, + imageOption, + imageAsIsOption, + descriptionOption, + jsonOption, + titleOption, + updateJsonOption, + outputOption }; setCommand.SetAction(result => Environment.ExitCode = SetFunction( result.GetRequiredValue(inputArgument), result.GetValue(formatOption), result.GetValue(imageOption), + result.GetValue(imageAsIsOption), result.GetValue(descriptionOption), result.GetValue(jsonOption), result.GetValue(titleOption), diff --git a/Jpeg.cs b/Jpeg.cs index 906a2f1..74bc1fe 100644 --- a/Jpeg.cs +++ b/Jpeg.cs @@ -1,4 +1,5 @@ using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; namespace RagePhoto.Cli; @@ -6,7 +7,11 @@ namespace RagePhoto.Cli; internal class Jpeg { internal static Byte[] GetEmptyJpeg(PhotoFormat format, out Size size) { - size = format == PhotoFormat.GTA5 ? new(960, 536) : new(1920, 1080); + size = format switch { + PhotoFormat.GTA5 => new(960, 536), + PhotoFormat.RDR2 => new(1920, 1080), + _ => throw new ArgumentException("Invalid Format", nameof(format)) + }; using Image image = new(size.Width, size.Height); image.ProcessPixelRows(static pixelAccessor => { for (Int32 y = 0; y < pixelAccessor.Height; y++) { @@ -16,27 +21,43 @@ internal class Jpeg { } } }); - using MemoryStream output = new(); - image.SaveAsJpeg(output, new() { + using MemoryStream jpegStream = new(); + image.SaveAsJpeg(jpegStream, new() { Quality = 100, ColorType = JpegEncodingColor.YCbCrRatio444 }); - return output.ToArray(); + return jpegStream.ToArray(); } - internal static Byte[] GetJpeg(Stream stream, out Size size) { - using Image image = Image.Load(stream); - size = image.Size; - image.Metadata.ExifProfile = null; - using MemoryStream output = new(); - image.SaveAsJpeg(output, new() { - Quality = 100, - ColorType = JpegEncodingColor.YCbCrRatio444 - }); - return output.ToArray(); + internal static Byte[] GetJpeg(Stream input, bool imageAsIs, out Size size) { + if (!imageAsIs) { + using Image image = Image.Load(input); + size = image.Size; + image.Metadata.ExifProfile = null; + using MemoryStream jpegStream = new(); + image.SaveAsJpeg(jpegStream, new() { + Quality = 100, + ColorType = JpegEncodingColor.YCbCrRatio444 + }); + return jpegStream.ToArray(); + } + else { + using MemoryStream jpegStream = new(); + input.CopyTo(jpegStream); + Byte[] jpeg = jpegStream.ToArray(); + size = GetSize(jpeg); + return jpeg; + } } internal static Size GetSize(ReadOnlySpan jpeg) { - return Image.Identify(jpeg).Size; + try { + return Image.Identify(new DecoderOptions { + Configuration = new(new JpegConfigurationModule()) + }, jpeg).Size; + } + catch (UnknownImageFormatException exception) { + throw new Exception("Unsupported Image Format", exception); + } } }