public static HybridMap2 LoadFromDisk(string dir, IFontProvider f, IPixelFormatProvider pf) { HybridMap2 ret = new HybridMap2(); Log.WriteLine("Loading charinfo..."); string sci = System.IO.File.ReadAllText(System.IO.Path.Combine(dir, "CharInfo.json")); ret.CharInfo = Newtonsoft.Json.JsonConvert.DeserializeObject <CharInfo[]>(sci); Log.WriteLine("Loading DistinctMappedChars..."); string sdmc = System.IO.File.ReadAllText(System.IO.Path.Combine(dir, "DistinctMappedChars.json")); ret.DistinctMappedChars = Newtonsoft.Json.JsonConvert.DeserializeObject <CharInfo[]>(sdmc); ret.FontProvider = f; ret.PixelFormatProvider = pf; return(ret); }
public unsafe HybridMap2(IFontProvider fontProvider, IPixelFormatProvider pixelFormatProvider, string fullMapPath, string refMapPath, string refFontPath, int coreCount, int partitionsPerDim, int?partitionDepth) { if (coreCount < 1) { coreCount = System.Environment.ProcessorCount - coreCount; } this.FontProvider = fontProvider; this.PixelFormatProvider = pixelFormatProvider; this.FontProvider.Init(this.PixelFormatProvider.DiscreteNormalizedValues.Length); Log.WriteLine(" DiscreteNormalizedValues:"); for (int i = 0; i < PixelFormatProvider.DiscreteNormalizedValues.Length; ++i) { if (i > 14) { Log.WriteLine(" ..."); break; } Log.WriteLine(" {0}: {1,10:0.00}", i, PixelFormatProvider.DiscreteNormalizedValues[i]); } Log.WriteLine("Number of source chars (1d): " + this.FontProvider.CharCount.ToString("N0")); Log.WriteLine("Chosen values per tile: " + pixelFormatProvider.DiscreteNormalizedValues.Length); Log.WriteLine("Dimensions: " + PixelFormatProvider.DimensionCount); Log.WriteLine("Resulting map will have this many entries: " + pixelFormatProvider.MapEntryCount.ToString("N0")); long mapdimpix = (long)Math.Sqrt(pixelFormatProvider.MapEntryCount); Log.WriteLine("Resulting map will be about: [" + mapdimpix.ToString("N0") + ", " + mapdimpix.ToString("N0") + "]"); // fill in char source info (actual tile values) Log.EnterTask("Analyze incoming font"); this.CharInfo = new CharInfo[FontProvider.CharCount];// new List<CharInfo>(); for (int ichar = 0; ichar < FontProvider.CharCount; ++ichar) { var ci = new CharInfo(PixelFormatProvider.DimensionCount) { srcIndex = ichar, #if DEBUG //fontImageCellPos = FontProvider.GetCharPosInChars(ichar), //fontImagePixelPos = FontProvider.GetCharOriginInPixels(ichar), #endif }; pixelFormatProvider.PopulateCharColorData(ci, fontProvider); this.CharInfo[ichar] = ci; } Log.WriteLine("Number of source chars: " + this.CharInfo.Length); PartitionManager pm = new PartitionManager(partitionsPerDim, partitionDepth.GetValueOrDefault(PixelFormatProvider.DimensionCount + 1), PixelFormatProvider.DiscreteNormalizedValues); // create list of all mapkeys Log.EnterTask("Generating {0:N0} map key indices", pixelFormatProvider.MapEntryCount); this.Keys = Utils.Permutate(PixelFormatProvider.DimensionCount, pixelFormatProvider.DiscreteNormalizedValues); // returns sorted. // populate the denormalized values for (int i = 0; i < this.Keys.Length; ++i) { this.Keys[i].DenormalizedValues = this.PixelFormatProvider.Denormalize(this.Keys[i].NormalizedValues); } Log.EndTask(); Log.WriteLine("Key count: " + this.Keys.Length); foreach (var ci in this.CharInfo) { pm.AddItem(ci); } Log.EndTask(); Log.EnterTask("Calculate all mappings"); // - generate a list of mappings and their distances ulong theoreticalMappings = (ulong)this.CharInfo.Length * (ulong)pixelFormatProvider.MapEntryCount; Log.WriteLine(" Theoretical mapping count: " + theoreticalMappings.ToString("N0")); List <Task> comparisonBatches = new List <Task>(); var Map = new Mapping[Keys.Length];// indices need to be synchronized with Keys. for (int icore = 0; icore < coreCount; ++icore) { // create a task to process a segment of keys ulong keyBegin = (ulong)icore; keyBegin *= (ulong)Keys.Length; keyBegin /= (ulong)coreCount; ulong keyEnd = (ulong)icore + 1; keyEnd *= (ulong)Keys.Length; keyEnd /= (ulong)coreCount; int coreID = icore; bool isLastCore = (icore == coreCount - 1); comparisonBatches.Add(Task.Run(() => { PopulateMap(keyBegin, keyEnd, coreID, isLastCore, pm, Map); })); } Task.WaitAll(comparisonBatches.ToArray()); Log.EndTask(); // todo: a histogram would be more informative than this crap. int numCharsUsed = 0; int numCharsUsedOnce = 0; CharInfo mostUsedChar = null; int numRepetitions = 0; foreach (var ci in this.CharInfo) { if (mostUsedChar == null || mostUsedChar.usages < ci.usages) { mostUsedChar = ci; } if (ci.usages > 0) { numCharsUsed++; } if (ci.usages == 1) { numCharsUsedOnce++; } if (ci.usages > 1) { numRepetitions += ci.usages - 1; } } Log.WriteLine("Process currently using {0:0.00} mb of memory)", Utils.BytesToMb(Utils.UsedMemoryBytes)); #if false OutputFullMap(fullMapPath, Map); #endif DistinctMappedChars = Map.DistinctBy(o => o.icharInfo).Select(o => this.CharInfo[o.icharInfo]).ToArray(); for (int ichar = 0; ichar < DistinctMappedChars.Length; ++ichar) { CharInfo ci = DistinctMappedChars[ichar]; Debug.Assert(ci != null); ci.refFontIndex = ichar; } OutputRefMapAndFont(refMapPath, refFontPath, Map); // save data structures string jsonDistinctMappedChars = Newtonsoft.Json.JsonConvert.SerializeObject(DistinctMappedChars, Newtonsoft.Json.Formatting.Indented); System.IO.File.WriteAllText(System.IO.Path.Combine(refMapPath, "..\\DistinctMappedChars.json"), jsonDistinctMappedChars); //string jsonKeys = Newtonsoft.Json.JsonConvert.SerializeObject(Keys, Newtonsoft.Json.Formatting.Indented); //System.IO.File.WriteAllText(System.IO.Path.Combine(refMapPath, "..\\Keys.json"), jsonKeys); string jsonCharInfo = Newtonsoft.Json.JsonConvert.SerializeObject(CharInfo, Newtonsoft.Json.Formatting.Indented); System.IO.File.WriteAllText(System.IO.Path.Combine(refMapPath, "..\\CharInfo.json"), jsonCharInfo); //string jsonMap = Newtonsoft.Json.JsonConvert.SerializeObject(Map, Newtonsoft.Json.Formatting.Indented); //System.IO.File.WriteAllText(System.IO.Path.Combine(refMapPath, "..\\Map.json"), jsonMap); Log.WriteLine("Post-map stats:"); Log.WriteLine(" Used char count: " + numCharsUsed); Log.WriteLine(" Number of unused char: " + (this.CharInfo.Length - numCharsUsed)); Log.WriteLine(" Number of chars used exactly once: " + numCharsUsedOnce); Log.WriteLine(" Most-used char: " + mostUsedChar + " (" + mostUsedChar.usages + ") usages"); Log.WriteLine(" Number of total char repetitions: " + numRepetitions); }
static void Main2(string[] args) { #if !DEBUG try { #else { #endif Log.WriteLine("----------------------------------------"); //PartitionManager partitionManager = null;// = new PartitionManager(1, 1, discreteValues); int partitionsPerDimension = 1; int?partitionDepth = null; IPixelFormatProvider pixelFormat = null; IFontProvider fontProvider = null; string outputDir = null; List <string> processImages = new List <string>(); int coresToUtilize = System.Environment.ProcessorCount; List <System.Drawing.Color> testColors = new List <System.Drawing.Color>(); args.ProcessArg(new string[] { "-help", "-?", "-h" }, s => { var assem = System.Reflection.Assembly.GetExecutingAssembly();// typeof(PartitionManager).Assembly; var ns = assem.EntryPoint.DeclaringType.Namespace; using (Stream stream = assem.GetManifestResourceStream(ns + ".cmdhelp.txt")) { using (var reader = new StreamReader(stream)) { while (!reader.EndOfStream) { string ln = reader.ReadLine(); Log.WriteLine(ln); } } } }); bool didluts = false; args.ProcessArg("-createlut", s => { var colorSpace = Utils.ParseRequiredLCCColorSpaceArgs(args, true); string outfile = null; args.ProcessArg("-o", s2 => { outfile = s2; }); List <System.Drawing.Color> palette = new List <System.Drawing.Color>(); string paletteName = null; args.ProcessArg("-palette", s2 => { paletteName = s2; palette.AddRange(Utils.GetNamedPalette(s2)); }); int levels = 32; args.ProcessArg("-levels", s2 => { levels = int.Parse(s2); }); bool useChroma = false; args.ProcessArg("-lcc", s2 => { useChroma = true; }); bool neutral = false; args.ProcessArg("-neutral", s2 => { neutral = true; }); CreateLUT(paletteName, palette.ToArray(), outfile, colorSpace, levels, useChroma, neutral); didluts = true; return; }); if (didluts) { return; } args.ProcessArg("-listpalettes", s => { Log.WriteLine("Listing palettes:"); foreach (var p in typeof(Palettes).GetProperties()) { Log.WriteLine(" {0}", p.Name); } }); args.ProcessArg("-viewpalette", s => { Log.WriteLine("Listing palette entries for palette:"); Log.WriteLine("{0}", s); var p = Utils.GetNamedPalette(s); Log.WriteLine("{0} entries", p.Length); for (int i = 0; i < p.Length; ++i) { var c = p[i]; Log.WriteLine("{0:000}: {1} {2}", i, ColorTranslator.ToHtml(c), ColorMapper.GetNearestName(c)); } }); args.ProcessArg("-argsfile", s => { Log.WriteLine("Reading args from file: {0}" + s); var lines = System.IO.File.ReadAllLines(s) .Select(l => l.Split('#')[0]) // remove comments .Where(l => !string.IsNullOrWhiteSpace(l)); // remove empty lines args = lines.Concat(args).ToArray(); }); args.ProcessArg("-outdir", o => { outputDir = o; }); MapSource mapSource = MapSource.Create; args.ProcessArg("-testpalette", s => { testColors.AddRange(Utils.GetNamedPalette(s)); }); args.ProcessArg("-testcolor", s => { testColors.Add(System.Drawing.ColorTranslator.FromHtml(s)); }); args.ProcessArg("-partitions", s => { if (s.Contains('x')) { partitionsPerDimension = int.Parse(s.Split('x')[0]); partitionDepth = int.Parse(s.Split('x')[1]); } else { partitionsPerDimension = int.Parse(s); } }); args.ProcessArg("-processImagesInDir", o => { var files = System.IO.Directory.EnumerateFiles(o, "*", System.IO.SearchOption.TopDirectoryOnly); foreach (var file in files) { processImages.Add(file); } }); args.ProcessArg("-processImage", o => { processImages.Add(o); }); args.ProcessArg("-cores", o => { int a = int.Parse(o); if (a < 1) { a = System.Environment.ProcessorCount - a; } coresToUtilize = a; }); FontFamilyFontProvider fontFamilyProvider = null; args.ProcessArg("-fonttype", s => { switch (s.ToLowerInvariant()) { case "mono": fontProvider = MonoPaletteFontProvider.ProcessArgs(args); break; case "normal": fontProvider = FontProvider.ProcessArgs(args); break; case "colorkey": fontProvider = ColorKeyFontProvider.ProcessArgs(args); break; case "fontfamily": fontProvider = fontFamilyProvider = FontFamilyFontProvider.ProcessArgs(args); break; default: throw new Exception("Unknown font type: " + s); } }); args.ProcessArg("-pf", s => { switch (s.ToLowerInvariant()) { case "square": pixelFormat = SquareLCCPixelFormat.ProcessArgs(args);// HSLPixelFormat.ProcessArgs(args); break; default: case "fivetile": pixelFormat = FiveTilePixelFormat.ProcessArgs(args, fontProvider); break; } }); if (pixelFormat == null) { Log.WriteLine("Pixel format not specified."); return; //pixelFormat = NaiveYUVPixelFormat.ProcessArgs(args); } // emoji12-C64_YUV-2v5x5+2 if (fontProvider == null) { Log.WriteLine("Font information not specified."); return; } if (pixelFormat == null) { Log.WriteLine("Pixel format not specified."); return; } args.ProcessArg("-calcn", s => { ulong maxMapKeys = ulong.Parse(s); //partitionManager.Init(); //ulong partitionCount = (ulong)partitionManager.PartitionCount; //Log.WriteLine("Partition count: {0:N0}", partitionCount); // so the thing about partition count. You can't just divide by partition count, // because in deeper levels most partitions are simply unused / empty. // a decent conservative approximation is to take the first N levels //partitionCount = (ulong)Math.Pow(partitionManager.PartitionsPerDimension, 2.5);// n = 2.5 //Log.WriteLine("Adjusted partition count: {0:N0}", partitionCount); Log.WriteLine("Charset count: {0:N0}", fontProvider.CharCount); Log.WriteLine("Cores to utilize: {0:N0}", coresToUtilize); Log.WriteLine("Luma + chroma components: {0:N0}", pixelFormat.DimensionCount); ulong NbasedOnMapSize = (ulong)Math.Floor(Math.Pow(maxMapKeys, 1.0 / pixelFormat.DimensionCount)); Log.WriteLine("======================"); Log.WriteLine("== THEREFORE, use N={0:N0}", NbasedOnMapSize); Log.WriteLine("======================"); System.Environment.Exit(0); }); if (outputDir == null) { Log.WriteLine("No output directory was specified."); return; } outputDir = System.IO.Path.GetFullPath(outputDir); Log.WriteLine("Output directory: {0}", outputDir); if (!System.IO.Directory.Exists(outputDir)) { System.IO.Directory.CreateDirectory(outputDir); } args.ProcessArg("-loadmap", _ => { mapSource = MapSource.Load; // if you're loading, then we want to process the args from that directory. //outputDir = System.IO.Path.GetDirectoryName(s); string argspath = System.IO.Path.Combine(outputDir, "args.txt"); var lines = System.IO.File.ReadAllLines(argspath) .Select(l => l.Split('#')[0]) // remove comments .Where(l => !string.IsNullOrWhiteSpace(l)); // remove empty lines args = lines.Concat(args).ToArray(); }); string partitionConfigTag = string.Format("p{0}x{1}", partitionsPerDimension, partitionDepth.HasValue ? partitionDepth.ToString() : "N"); string configTag = string.Format("{0}_{1}_{2}", fontProvider.DisplayName, pixelFormat.PixelFormatString, partitionConfigTag); outputDir = System.IO.Path.Combine(outputDir, configTag); Log.WriteLine("Ensuring directory exists: {0}", outputDir); System.IO.Directory.CreateDirectory(outputDir); string logPath = System.IO.Path.Combine(outputDir, "log.txt"); Log.SetLogFile(logPath); string infopath = System.IO.Path.Combine(outputDir, "args.txt"); string mapFullPath = System.IO.Path.Combine(outputDir, string.Format("mapfull_{0}.png", configTag)); string mapRefPath = System.IO.Path.Combine(outputDir, string.Format("mapref_{0}.png", configTag)); string mapFontPath = System.IO.Path.Combine(outputDir, string.Format("mapfont_{0}.png", configTag)); args.ProcessArg("-loadOrCreateMap", _ => { if (System.IO.File.Exists(mapRefPath) && System.IO.File.Exists(mapFontPath)) { Log.WriteLine("-loadOrCreateMap: Loading existing map."); mapSource = MapSource.Load; } else { Log.WriteLine("-loadOrCreateMap: Looks like we have to create the map."); mapSource = MapSource.Create; } }); if (mapSource == MapSource.Create) { using (var infoFile = new StreamWriter(infopath)) { foreach (var arg in args) { infoFile.WriteLine(arg); } } } HybridMap2 map = null; switch (mapSource) { case MapSource.Create: Log.EnterTask("--- MAP GENERATION"); map = new HybridMap2(fontProvider, pixelFormat, mapFullPath, mapRefPath, mapFontPath, coresToUtilize, partitionsPerDimension, partitionDepth); Log.EndTask(); break; case MapSource.Load: Log.EnterTask("--- MAP LOAD"); map = HybridMap2.LoadFromDisk(outputDir, fontProvider, pixelFormat); Log.EndTask(); break; } if (fontFamilyProvider != null) { //string fontImgPath = System.IO.Path.Combine(outputDir, "font.png"); //fontFamilyProvider.SaveFontImage(fontImgPath); } Log.EnterTask("processing images"); foreach (var c in testColors) { map.TestColor(outputDir, ColorF.From(c)); } using (var refMapImage = new Bitmap(mapRefPath)) using (var refFontImage = new Bitmap(mapFontPath)) { foreach (var file in processImages) { Log.WriteLine("Processing {0}", file); string destFile = string.Format("test-{0}.png", System.IO.Path.GetFileNameWithoutExtension(file)); string destfullp = System.IO.Path.Combine(outputDir, destFile); using (var testImg = new Bitmap(file)) { var rv = map.ProcessImageUsingRef(refMapImage, refFontImage, testImg, destfullp); if (fontFamilyProvider != null) { string str = fontFamilyProvider.ConvertToText(rv); string txtpath = System.IO.Path.Combine(outputDir, string.Format("test-{0}.txt", System.IO.Path.GetFileNameWithoutExtension(file))); System.IO.File.WriteAllText(txtpath, str); } } } } Log.EndTask(); #if !DEBUG } catch (Exception e) { Log.WriteLine("Exception occurred:\r\n{0}", e); } #else }