diff --git a/.gitignore b/.gitignore index 8cec2ca..3b1834b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /EncodeAndSign/bin/Debug /EncodeAndSign/obj/Debug /EncodeAndSign/packages +/Flipnote Tools Desktop diff --git a/EncodeAndSign/.vs/EncodeAndSign/v16/.suo b/EncodeAndSign/.vs/EncodeAndSign/v16/.suo index 6aadd2d..c556ed1 100644 Binary files a/EncodeAndSign/.vs/EncodeAndSign/v16/.suo and b/EncodeAndSign/.vs/EncodeAndSign/v16/.suo differ diff --git a/EncodeAndSign/Config.cs b/EncodeAndSign/Config.cs new file mode 100644 index 0000000..a60343b --- /dev/null +++ b/EncodeAndSign/Config.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; + +namespace EncodeAndSign +{ + [JsonObject] + public class Config + { + public int DitheringMode { get; set; } + public bool Accurate { get; set; } + public bool Split { get; set; } + + public Config() { } + } +} diff --git a/EncodeAndSign/Data/DecodedFrame.cs b/EncodeAndSign/Data/DecodedFrame.cs index 4cabbe3..57a1df0 100644 --- a/EncodeAndSign/Data/DecodedFrame.cs +++ b/EncodeAndSign/Data/DecodedFrame.cs @@ -493,7 +493,7 @@ public void CreateThumbnail() } } Thumbnail.WritePixels(new Int32Rect(0, 0, 128, 96), thumbnailPixels, 128, 0); - + } byte GetCompositeThumbnailPixel(int x, int y) @@ -529,7 +529,6 @@ public byte[] CreateThumbnailW64() var bruhh = BitmapImage2Bitmap(Thumbnail); var resized = new System.Drawing.Bitmap(bruhh, new System.Drawing.Size(bruhh.Width / 2, bruhh.Height / 2)); var res = new byte[1536]; - /// TO DO : change with the actual frame thumbnail for (int x = 0; x < 64; x++) { for (int y = 0; y < 48; y++) @@ -538,17 +537,19 @@ public byte[] CreateThumbnailW64() if (b < 0.1) { w64SetPixel(res, x, y, TColor.Black); - } else if(b > 0.8) + } + else if (b > 0.8) { w64SetPixel(res, x, y, TColor.White); - } else + } + else { w64SetPixel(res, x, y, TColor.Gray); } } } - - + + return res; } diff --git a/EncodeAndSign/Data/Flipnote.cs b/EncodeAndSign/Data/Flipnote.cs index 621c8f1..4fa0233 100644 --- a/EncodeAndSign/Data/Flipnote.cs +++ b/EncodeAndSign/Data/Flipnote.cs @@ -290,7 +290,7 @@ public void Save(string fn) { AnimationDataSize = (uint)(AnimationDataSize + 8 + Frames.Count() * 4); var AllignSize = (uint)(4 - ((0x6A0 + AnimationDataSize + Frames.Count()) % 4)); - if (AllignSize != 4 && AllignSize !=2) + if (AllignSize != 4) AnimationDataSize += AllignSize; w.Write(FileMagic); w.Write(AnimationDataSize); @@ -337,7 +337,7 @@ public void Save(string fn) for (int i = 0; i < Frames.Length; i++) w.Write((byte)0); - if (AllignSize != 4 && AllignSize != 2) + if (AllignSize != 4) w.Write(new byte[AllignSize]); // make the next offset dividable by 4; diff --git a/EncodeAndSign/Data/FrameDecoder.cs b/EncodeAndSign/Data/FrameDecoder.cs index e90df57..ee9507f 100644 --- a/EncodeAndSign/Data/FrameDecoder.cs +++ b/EncodeAndSign/Data/FrameDecoder.cs @@ -23,6 +23,23 @@ public FrameDecoder(string[] filenames) BoolFrames = bools; } + public FrameDecoder(List bitmaps) + { + //frames to bool arrays + List bools = new List(); + int i = 0; + bitmaps.ToList().ForEach(frame => + { + var color = frame; + var bw = color.Clone(new Rectangle(0, 0, color.Width, color.Height), PixelFormat.Format1bppIndexed); + bools.Add(BitmapToBoolArray(bw)); + + }); + BoolFrames = bools; + } + + + public static bool[,] BitmapToBoolArray(Bitmap PiecesBitmap) { diff --git a/EncodeAndSign/EncodeAndSign.csproj b/EncodeAndSign/EncodeAndSign.csproj index 15b2b06..9e2b5e7 100644 --- a/EncodeAndSign/EncodeAndSign.csproj +++ b/EncodeAndSign/EncodeAndSign.csproj @@ -37,6 +37,9 @@ + + packages\LibDithering.0.9.3\lib\netstandard2.0\LibDithering.dll + packages\Microsoft.Win32.Registry.4.7.0\lib\net461\Microsoft.Win32.Registry.dll @@ -61,13 +64,35 @@ packages\NAudio.WinMM.2.0.0\lib\netstandard2.0\NAudio.WinMM.dll + + packages\NDesk.Options.Core.1.2.5\lib\net472\NDesk.Options.dll + + + packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + packages\ShellProgressBar.5.1.0\lib\net461\ShellProgressBar.dll + + packages\SixLabors.ImageSharp.1.0.3\lib\net472\SixLabors.ImageSharp.dll + + + packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll True @@ -93,6 +118,7 @@ + @@ -100,6 +126,7 @@ + diff --git a/EncodeAndSign/Encoder/Encoder.cs b/EncodeAndSign/Encoder/Encoder.cs index ff0131d..c038ca6 100644 --- a/EncodeAndSign/Encoder/Encoder.cs +++ b/EncodeAndSign/Encoder/Encoder.cs @@ -1,5 +1,6 @@ using EncodeAndSign.Data; using System.Collections.Generic; +using System.Drawing; namespace EncodeAndSign.Encoder { @@ -17,6 +18,17 @@ public Encoder(string[] frames, Flipnote dummy) ResultNote = encoded; } + public Encoder(List frames, Flipnote dummy) + { + + FrameDecoder FrameDecoder = new FrameDecoder(frames); + + Flipnote encoded = Flipnote.New(dummy.Metadata.CurrentAuthorName, dummy.Metadata.CurrentAuthorId, ToDecodedFrame(FrameDecoder.BoolFrames), false); + + //todo: use this for metadata display i guess + ResultNote = encoded; + } + public List ToDecodedFrame(List input) { List buffer = new List(); diff --git a/EncodeAndSign/Encoder/ImageEncoder.cs b/EncodeAndSign/Encoder/ImageEncoder.cs new file mode 100644 index 0000000..9a9a3a6 --- /dev/null +++ b/EncodeAndSign/Encoder/ImageEncoder.cs @@ -0,0 +1,83 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Dithering; +using System.Collections.Generic; +using System.Drawing; + +namespace EncodeAndSign.Encoder +{ + public class ImageEncoder + { + public ImageEncoder() + { + + } + + public void DoRinDithering(string[] filenames, int type) + { + List bitmaps = new List(); + IDither DitheringType = null; + switch (type) + { + case 0: + DitheringType = KnownDitherings.Bayer8x8; + break; + case 1: + DitheringType = KnownDitherings.Bayer4x4; + break; + case 2: + DitheringType = KnownDitherings.Bayer2x2; + break; + case 3: + DitheringType = KnownDitherings.FloydSteinberg; + break; + case 4: + DitheringType = KnownDitherings.Atkinson; + break; + case 5: + DitheringType = KnownDitherings.Burks; + break; + case 6: + DitheringType = KnownDitherings.JarvisJudiceNinke; + break; + case 7: + DitheringType = KnownDitherings.Sierra3; + break; + case 8: + DitheringType = KnownDitherings.StevensonArce; + break; + case 9: + DitheringType = KnownDitherings.Sierra2; + break; + case 10: + DitheringType = KnownDitherings.Sierra3; + break; + case 11: + DitheringType = KnownDitherings.SierraLite; + break; + case 12: + DitheringType = KnownDitherings.Stucki; + break; + case 13: + DitheringType = KnownDitherings.Ordered3x3; + break; + default: + //this one is my favorite :) + DitheringType = KnownDitherings.Bayer8x8; + break; + } + for (int i = 0; i < filenames.Length; i++) + { + using (SixLabors.ImageSharp.Image image = SixLabors.ImageSharp.Image.Load(filenames[i])) + { + image.Mutate(x => + { + x.BinaryDither(DitheringType); + }); + image.SaveAsPng($"frames/frame_{i}.png"); + image.Dispose(); + } + } + } + } +} diff --git a/EncodeAndSign/Program.cs b/EncodeAndSign/Program.cs index ee90b0c..de685a1 100644 --- a/EncodeAndSign/Program.cs +++ b/EncodeAndSign/Program.cs @@ -1,7 +1,10 @@ using EncodeAndSign.Data; +using EncodeAndSign.Encoder; +using Newtonsoft.Json; using ShellProgressBar; using System; using System.Diagnostics; +using System.Drawing; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -25,8 +28,44 @@ class Program private static object _syncLock = new object(); private static AutoResetEvent _waitHandle = new AutoResetEvent(false); + private static Config config = null; + static void Main(string[] args) { + if (!File.Exists("config.json")) + { + var newConfig = new Config(); + newConfig.Accurate = true; + newConfig.DitheringMode = 1; + newConfig.Split = false; + config = newConfig; + + + JsonSerializer serializer = new JsonSerializer(); + serializer.Formatting = Formatting.Indented; + serializer.NullValueHandling = NullValueHandling.Ignore; + + using (StreamWriter sw = new StreamWriter("config.json")) + using (JsonWriter writer = new JsonTextWriter(sw)) + { + serializer.Serialize(writer, newConfig); + // {"ExpiryDate":new Date(1230375600000),"Price":0} + } + } + else + { + JsonSerializer serializer = new JsonSerializer(); + serializer.Formatting = Formatting.Indented; + serializer.NullValueHandling = NullValueHandling.Ignore; + + using (StreamReader sw = new StreamReader("config.json")) + using (JsonReader writer = new JsonTextReader(sw)) + { + var read = serializer.Deserialize(writer); + config = read; + // {"ExpiryDate":new Date(1230375600000),"Price":0} + } + } AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); // start the message pumping thread Thread msgThread = new Thread(MessagePump); @@ -35,8 +74,12 @@ static void Main(string[] args) string command = string.Empty; do { - //do the flipnote stuff - doShit(); + + //do Encoding + + CreateAndSignFlipnote(); + + SetQuitRequested(); } while (!_quitRequested); // signal that we want to quit @@ -74,7 +117,7 @@ static void CurrentDomain_ProcessExit(object sender, EventArgs e) Console.WriteLine("exit"); } - private static void doShit() + private static void CreateAndSignFlipnote() { //no input, stop if (!File.Exists("frames/input.mp4")) return; @@ -98,150 +141,201 @@ private static void doShit() CollapseWhenFinished = false, ProgressCharacter = '─' }; + Bitmap[] bitmaps = null; using (var pbar = new ProgressBar(totalTicks, "Flipnote Progress", options)) { - + //frame stuff if (!File.Exists("frames/frame_0.png")) { //todo: Stop running console commands - //this code sucks. - //Turn video into 30FPS, fixed sound sync issues (https://github.com/khang06/dsiflipencode/pull/5) - Process framerate = new Process(); - ProcessStartInfo frameinfo = new ProcessStartInfo(); - - frameinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - frameinfo.FileName = "cmd.exe"; - frameinfo.Arguments = @"/C cd ffmpeg/bin && ffmpeg -i ..\..\frames\input.mp4 -filter:v fps=30 ..\..\frames\frame_%d.png"; - frameinfo.RedirectStandardOutput = false; - - framerate.StartInfo = frameinfo; - framerate.Start(); - - pbar.Tick(); - using (var child = pbar.Spawn(totalTicks, "Changing Framerate...", childOptions)) + if (config.Accurate) { - child.Tick(2); - framerate.WaitForExit(); - framerate.Dispose(); - - - - Process framesplit = new Process(); - ProcessStartInfo startInfo = new ProcessStartInfo(); - - startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - startInfo.FileName = "cmd.exe"; - startInfo.Arguments = @"/C cd ffmpeg/bin && ffmpeg -i ..\..\frames\frame_%d.png -vf scale=256:192 ..\..\frames\frame_%d.png"; - startInfo.RedirectStandardOutput = false; - framesplit.StartInfo = startInfo; - framesplit.Start(); - child.Tick(5, "Splitting Frames..."); - framesplit.WaitForExit(); - framesplit.Dispose(); - child.Tick(10, "Frames Split!"); + using (var child = pbar.Spawn(2, "Mode: Accurate, Splitting Frames...", childOptions)) + { + #region Create30FPSframes + Process framerate = new Process(); + ProcessStartInfo frameinfo = new ProcessStartInfo(); + frameinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + frameinfo.FileName = "cmd.exe"; + frameinfo.Arguments = @"/C cd ffmpeg/bin && ffmpeg -i ..\..\frames\input.mp4 -filter:v fps=30 ..\..\frames\frame_%d.png"; + frameinfo.RedirectStandardOutput = false; + framerate.StartInfo = frameinfo; + framerate.Start(); + framerate.WaitForExit(); + framerate.Dispose(); + #endregion + child.Tick(1, "Mode: Accurate, Resizing Frames..."); + #region Scale30FPSFrames + Process framesplit = new Process(); + ProcessStartInfo startInfo = new ProcessStartInfo(); + + startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + startInfo.FileName = "cmd.exe"; + startInfo.Arguments = @"/C cd ffmpeg/bin && ffmpeg -i ..\..\frames\frame_%d.png -vf scale=256:192 ..\..\frames\frame_%d.png"; + startInfo.RedirectStandardOutput = false; + framesplit.StartInfo = startInfo; + framesplit.Start(); + framesplit.WaitForExit(); + framesplit.Dispose(); + #endregion + child.Tick(2, "Mode: Accurate, Frames Split!"); + } + pbar.Tick(1); + } + else + { + using (var child = pbar.Spawn(2, "Mode: Fast", childOptions)) + { + child.Tick(1, "Mode: Fast, Splitting Frames..."); + #region SplitFrames + Process framerate = new Process(); + ProcessStartInfo frameinfo = new ProcessStartInfo(); + frameinfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + frameinfo.FileName = "cmd.exe"; + frameinfo.Arguments = @"/C cd ffmpeg/bin && ffmpeg -i ..\..\frames\input.mp4 -vf scale=256:192 ..\..\frames\frame_%d.png"; + frameinfo.RedirectStandardOutput = false; + framerate.StartInfo = frameinfo; + framerate.Start(); + framerate.WaitForExit(); + framerate.Dispose(); + #endregion + child.Tick(2, "Mode: Fast, Frames Split!..."); + } + pbar.Tick(1); } - File.Copy("frames/frame_1.png", "frames/frame_0.png"); - Process dither = new Process(); - ProcessStartInfo ditherInfo = new ProcessStartInfo(); - - ditherInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - ditherInfo.FileName = "cmd.exe"; - ditherInfo.Arguments = @"/C cd magick && magick mogrify -format png -colors 2 -type bilevel ..\frames\*.png"; - ditherInfo.RedirectStandardOutput = false; - - dither.StartInfo = ditherInfo; - dither.Start(); - - pbar.Tick(); - using (var child = pbar.Spawn(totalTicks, "Dithering...", childOptions)) + #region Dithering + string[] Bitmapframes = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + "/frames", "*.png"); + NumericalSort(Bitmapframes); + var imageEncoder = new ImageEncoder(); + var mode = config.DitheringMode; + string modestring = String.Empty; + using (var child = pbar.Spawn(2, "Dithering...", childOptions)) { - child.Tick(5); - dither.WaitForExit(); - dither.Dispose(); - child.Tick(10, "Dithered!"); + switch (mode) + { + case 0: + child.Tick(1, "Dithering Mode: None."); + bitmaps = null; + child.Tick(2, "Dithering Mode: None."); + break; + case 14: + child.Tick(1, "Dithering Mode: imagemagick bilevel..."); + bitmaps = null; + #region mogrify + + Process dither = new Process(); + ProcessStartInfo ditherInfo = new ProcessStartInfo(); + + ditherInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + ditherInfo.FileName = "cmd.exe"; + ditherInfo.Arguments = "/C mogrify -format png -colors 2 -type bilevel frames/*.png"; + ditherInfo.RedirectStandardOutput = false; + dither.StartInfo = ditherInfo; + dither.Start(); + dither.WaitForExit(); + dither.Dispose(); + #endregion + child.Tick(2, "Dithering Mode: imagemagick bilevel, Done!"); + break; + default: + child.Tick(1, $"Dithering mode: {mode}"); + imageEncoder.DoRinDithering(Bitmapframes, mode); + child.Tick(2, $"Dithering mode: {mode}, Done!"); + break; + } + + pbar.Tick(2); } + #endregion + - } - else - { - pbar.Tick(2); } + pbar.Tick(2); + if (!File.Exists("frames/audio.wav")) { - Process audio = new Process(); - ProcessStartInfo audioInfo = new ProcessStartInfo(); - - audioInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - audioInfo.FileName = "cmd.exe"; - audioInfo.Arguments = @"/C cd ffmpeg/bin && ffmpeg -i ..\..\frames\input.mp4 -ac 1 -ar 8192 ..\..\frames\audio.wav"; - audioInfo.RedirectStandardOutput = false; - - audio.StartInfo = audioInfo; - audio.Start(); - - pbar.Tick(); - using (var child = pbar.Spawn(totalTicks, "Writing Wav...", childOptions)) + using (var child = pbar.Spawn(2, "Writing Audio...", childOptions)) { - child.Tick(5); + child.Tick(1); + #region audio + Process audio = new Process(); + ProcessStartInfo audioInfo = new ProcessStartInfo(); + + audioInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; + audioInfo.FileName = "cmd.exe"; + audioInfo.Arguments = @"/C cd ffmpeg/bin && ffmpeg -i ..\..\frames\input.mp4 -ac 1 -ar 8192 ..\..\frames\audio.wav"; + audioInfo.RedirectStandardOutput = false; + audio.StartInfo = audioInfo; + audio.Start(); audio.WaitForExit(); audio.Dispose(); - child.Tick(10, "Audio Written!"); + #endregion + child.Tick(2, "Audio Written!"); } - - } - else - { - pbar.Tick(); } + pbar.Tick(3); + - pbar.Tick(); + //ppm stuff - - using (var child = pbar.Spawn(totalTicks, "Generating PPM...", childOptions)) + //Get dummy flipnote + //File can be replaced by user to automatically embed their own account information. + using (var child = pbar.Spawn(4, "Generating Flipnote...", childOptions)) { - //Get dummy flipnote - //File can be replaced by user to automatically embed their own account information. + child.Tick(1, "Getting Dummy Information..."); string[] dummyPath = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + "/DummyFlipnote", "*.ppm"); Flipnote dummy = new Flipnote(dummyPath[0]); + pbar.Tick(4); + + child.Tick(2, "Getting Frame Data..."); //Get Frames and sort them - child.Tick(2); - string[] frames = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + "/frames", "*.png"); - NumericalSort(frames); - child.Tick(5); - //Create Encoder - var encoder = new Encoder.Encoder(frames, dummy); - child.Tick(8); + Encoder.Encoder encoder = null; + pbar.Tick(5); + if (bitmaps == null) + { + string[] frames = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + "/frames", "*.png"); + NumericalSort(frames); + child.Tick(3, "Encoding..."); + encoder = new Encoder.Encoder(frames, dummy); + } + else + { + child.Tick(3, "Encoding..."); + encoder = new Encoder.Encoder(bitmaps.ToList(), dummy); + } + pbar.Tick(6); + + + encoder.ResultNote.Save("out/" + encoder.ResultNote.Filename); + child.Tick(4, "Encoded!"); + if (encoder.ResultNote.Signed) { - pbar.Tick(10, "Finished! Flipnote: Signed! Press any key to exit."); - + pbar.Tick(10, "Finished! Flipnote: Signed. Press any key to exit."); } else { pbar.Tick(10, "Finished! Flipnote: Unsigned. Press any key to exit."); } - child.Tick(10, "PPM Written!"); - Console.ReadKey(); - return; - } + } + Console.ReadKey(); + return; - } - } catch (Exception e) { diff --git a/EncodeAndSign/packages.config b/EncodeAndSign/packages.config index 5003ac0..91293b5 100644 --- a/EncodeAndSign/packages.config +++ b/EncodeAndSign/packages.config @@ -1,5 +1,6 @@  + @@ -8,7 +9,14 @@ + + + + + + +