Illnab1024 Posted May 31, 2012 Share Posted May 31, 2012 Just relaxing, decided to make this for fun. Hidden Content: C# source using System; using System.Collections.Generic; using System.Text; using PaintDotNet; using PaintDotNet.Data; using PaintDotNet.Rendering; using System.IO; using System.Drawing; using System.Windows.Forms; using System.Drawing.Imaging; using System.Linq; namespace DotExeFileType { public class MyFileType : FileType { public MyFileType() : base("Exe File", FileTypeFlags.SupportsSaving, new String[] { ".exe" }) { } protected override void OnSave(Document input, Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback) { RenderArgs ra = new RenderArgs(new Surface(input.Size)); // input.Render(ra); input.Render(ra, false); var img = ra.Bitmap; Bitmap cells = new Bitmap(80 * 8, 25 * 12); Graphics ctx = Graphics.FromImage(cells); ctx.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; ctx.DrawImage(img, 0, 0, 80 * 8, 25 * 12); ctx.Dispose(); byte[] outArr = new byte[80*25*4]; // Downsample to 4-bit colors for (int y = 0; y < 25*12; y++) { for (int x = 0; x < 80*8; x++) { Color c = cells.GetPixel(x, y); cells.SetPixel(x, y, bestMatch(ColorBgra.FromColor(cells.GetPixel(x, y)))); } } // Histogram the 8x12 characters and find // the closest match for (int y = 0; y < 25; y++) { for (int x = 0; x < 80; x++) { Dictionary<ColorBgra, int> histo = new Dictionary<ColorBgra, int>(); for (int j = 0; j < 12; j++) { for (int i = 0; i < 8; i++) { ColorBgra col = ColorBgra.FromColor(cells.GetPixel(i + x * 8, j + y * 12)); if (!histo.ContainsKey(col)) histo.Add(col, 1); else histo[col]++; } } var histoList = histo .OrderByDescending(k => k.Value).Select(k => k.Key) .ToList(); byte foreground = (byte)Enumerable.Range(0, ConsoleColors.Length) .Where(i => ConsoleColors[i] == histoList[0]) .First(); byte background; if (histoList.Count > 1) { background = (byte)Enumerable.Range(0, ConsoleColors.Length) .Where(i => ConsoleColors[i] == histoList[1]) .First(); } else { background = foreground; } double ratio; if (histoList.Count > 1) ratio = 96 * histo[histoList[0]] / (histo[histoList[0]] + histo[histoList[1]]); else ratio = 0; int bestCodepoint = 0; double bestRatioDiff = Double.MaxValue; for (int i = 0; i < CP437.Length; i++) { if (Math.Abs((double)(CP437[i]) - ratio) < bestRatioDiff) { bestRatioDiff = Math.Abs((double)(CP437[i]) - ratio); bestCodepoint = i; } } var index = (y * 80 + x) * 4; outArr[index] = (byte)bestCodepoint; outArr[index + 1] = (byte)0; outArr[index + 2] = (byte)(foreground | (background << 4)); outArr[index + 3] = (byte)0; } } output.Write(ExeOut.OutExe, 0, (int)ArrayOffset); output.Write(outArr, 0, outArr.Length); output.Write(ExeOut.OutExe, (int)(ArrayOffset + outArr.Length), (int)(ExeOut.OutExe.Length - (ArrayOffset + outArr.Length))); } protected override Document OnLoad(Stream input) { throw new NotImplementedException(); } ColorBgra[] ConsoleColors = { ColorBgra.FromOpaqueInt32(0x000000), ColorBgra.FromOpaqueInt32(0x000080), ColorBgra.FromOpaqueInt32(0x008000), ColorBgra.FromOpaqueInt32(0x008080), ColorBgra.FromOpaqueInt32(0x800000), ColorBgra.FromOpaqueInt32(0x800080), ColorBgra.FromOpaqueInt32(0x808000), ColorBgra.FromOpaqueInt32(0xC0C0C0), ColorBgra.FromOpaqueInt32(0x808080), ColorBgra.FromOpaqueInt32(0x0000FF), ColorBgra.FromOpaqueInt32(0x00FF00), ColorBgra.FromOpaqueInt32(0x00FFFF), ColorBgra.FromOpaqueInt32(0xFF0000), ColorBgra.FromOpaqueInt32(0xFF00FF), ColorBgra.FromOpaqueInt32(0xFFFF00), ColorBgra.FromOpaqueInt32(0xFFFFFF) }; // Generated from CodeLab and a screenshot! int[] CP437 = { 00, 38, 60, 38, 32, 40, 44, 20, 76, 32, 64, 37, 32, 32, 48, 40, 29, 29, 30, 28, 47, 40, 21, 36, 24, 24, 15, 15, 13, 20, 32, 32, 00, 22, 14, 42, 30, 20, 40, 08, 18, 18, 24, 14, 08, 08, 06, 14, 45, 23, 30, 27, 32, 29, 30, 27, 38, 30, 12, 16, 18, 12, 18, 20, 40, 36, 41, 30, 38, 35, 33, 34, 38, 22, 28, 38, 30, 45, 45, 34, 33, 41, 40, 31, 26, 36, 34, 38, 34, 30, 35, 22, 14, 22, 12, 08, 24, 33, 20, 33, 24, 26, 32, 33, 22, 28, 32, 24, 30, 25, 24, 32, 32, 23, 20, 22, 25, 22, 26, 22, 28, 22, 20, 16, 20, 14, 24, 23 }; double colorDistance(ColorBgra a, ColorBgra { HsvColor aH = HsvColor.FromColor(a.ToColor()); HsvColor bH = HsvColor.FromColor(b.ToColor()); return Math.Abs(bH.Hue - aH.Hue) + Math.Abs(bH.Saturation - aH.Saturation) + Math.Abs(bH.Value - aH.Value); } ColorBgra bestMatch(ColorBgra c) { double bestDist = Double.MaxValue; ColorBgra bestCol = ColorBgra.Black; for (int n = 0; n < 16; n++) { double curDist = colorDistance(c, ConsoleColors[n]); if (curDist < bestDist) { bestCol = ConsoleColors[n]; bestDist = curDist; } } return bestCol; } uint ArrayOffset = 0x418; } public class MyFileTypeFactory : IFileTypeFactory { public FileType[] GetFileTypeInstances() { return new FileType[] { new MyFileType() }; } } } Hidden Content: c++ source #define WIN32_LEAN_AND_MEAN #include <Windows.h> // Stuff to pass WriteConsoleOutputW. These are in the // .data segment (0x2400 in the .exe) in whatever order // the compiler felt like. CONSOLE_SCREEN_BUFFER_INFO outInfo; COORD outSize = {80, 25}; COORD outPos = {0, 0}; // Const, so it will be in (because we merged .rdata // into it) .text at 0x400 in the .exe. // Totally note I got the order wrong here but it doesn't // really matter since we're filling in these values elsewhere. const CHAR_INFO outChar[80][25] = { { {L'0', 0xf} } }; // Custom entry-point to remind you not to link this // with the Visual C Run-Time library. int Main() { // Even though we use Unicode output, it's rare for // a Windows console to use Unicode. HANDLE conOut = GetStdHandle(STD_OUTPUT_HANDLE); HANDLE conIn = GetStdHandle(STD_INPUT_HANDLE ); GetConsoleScreenBufferInfo(conOut, &outInfo); outInfo.dwCursorPosition.X = 0; WriteConsoleOutputW(conOut, (CHAR_INFO*)outChar, outSize, outPos, &outInfo.srWindow); ReadConsoleW(conIn, (LPVOID)&outInfo, 1, 0, 0); outInfo.dwCursorPosition.Y += 25; SetConsoleCursorPosition(conOut, outInfo.dwCursorPosition); return 0; } Compile as x86, use /MERGE:".rdata=.text", /NODEFAULTLIB, and /MANIFEST:NO to link. Download: DotExeFileType.zip (Edited: Messed up the 'ratio' variable) Quote ~~ Link to comment Share on other sites More sharing options...
nitenurse79 Posted May 31, 2012 Share Posted May 31, 2012 How does this exactly work? Quote Link to comment Share on other sites More sharing options...
Illnab1024 Posted May 31, 2012 Author Share Posted May 31, 2012 How does this exactly work? In the DLL there's an embedded resource with the .exe produced by building the C code. That executable has a static array from `const CHAR_INFO outChar[80][25] = { { {L'0', 0xf} } };` which is printed directly to the console using ConsoleWriteW. The Plugin writes that .exe to file but writes over the static array inside the executable to change the "image" that gets written to the console. It's just a "for-fun" plugin; there's absolutely no point to saving an image as an executable file, and even though you could do it as a full-color bitmap and use Windows GDI stuff to display it, that's also pointless when there's Windows Photo Viewer. If I really wanted to have fun, I'd do a 16-bit .COM FileType using int 21 stuff, but I'm on a 64-bit system, so meh. Quote ~~ Link to comment Share on other sites More sharing options...
pyrochild Posted May 31, 2012 Share Posted May 31, 2012 Haha what even This is awesome and useless. Quote ambigram signature by Kemaru [i write plugins and stuff] If you like a post, upvote it! Link to comment Share on other sites More sharing options...
Rick Brewster Posted May 31, 2012 Share Posted May 31, 2012 Weeeeeee 1 Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
nitenurse79 Posted June 1, 2012 Share Posted June 1, 2012 Just had a play with this, it is as pyrochild says "This is awesome and useless" @Rick Brewster nice example Fun to be had Quote Link to comment Share on other sites More sharing options...
midora Posted June 1, 2012 Share Posted June 1, 2012 Useless? Maybe, but it could also be the base of a stand-alone slideshow using embedded images. Quote Link to comment Share on other sites More sharing options...
qwertyuu Posted July 27, 2012 Share Posted July 27, 2012 I think I'll actually process a whole wallpaper in this, and turn THAT into my wallpaper could be nice enough! Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.