package readers.fonts; import java.awt.image.BufferedImage; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.util.List; import java.awt.Color; public class Font { Glyph[] data = new Glyph[10]; final static int TRANSPARENT = new Color(0,0,0,0).getRGB(); final public static Font FONT_DDR_DIFF = LoadFont("ddr_diff"); final public static Font FONT_DDR_EXCOMBO = LoadFont("ddr_excombo"); final public static Font FONT_DDR_NOTECOUNT = LoadFont("ddr_noteCount"); final public static Font FONT_DDR_SCORE = LoadFont("ddr_score"); final public static Font FONT_ITG_DIFF = LoadFont("itg_diff"); final public static Font FONT_ITG_EX = LoadFont("itg_ex"); final public static Font FONT_ITG_NOTECOUNT = LoadFont("itg_noteCount"); final public static Font FONT_ITG_PCT = LoadFont("itg_pct"); final public static Font FONT_LOVELIVE_NOTECOUNT = LoadFont("lovelive_noteCount"); final public static Font FONT_LOVELIVE_PCT = LoadFont("lovelive_pct"); final public static Font FONT_LOVELIVE_SCORE = LoadFont("lovelive_score"); final public static Font FONT_POPN_COMBO = LoadFont("popn_combo"); final public static Font FONT_POPN_NOTECOUNT = LoadFont("popn_noteCount"); final public static Font FONT_POPN_SCORE = LoadFont("popn_score"); final public static Font FONT_SDVX_EXSCORE = LoadFont("sdvx_EXScore"); final public static Font FONT_SDVX_LITTLESCORE = LoadFont("sdvx_littleScore"); final public static Font FONT_SDVX_NOTECOUNT = LoadFont("sdvx_noteCount"); final public static Font FONT_SDVX_BIGSCORE = LoadFont("sdvx_largeScore"); public static Font LoadFont(String fontName) { Path f = Paths.get("readers","fonts",fontName); if (Files.exists(f)) { Font font = new Font(); try { List<String> data = Files.readAllLines(f); for (int i=0;i<data.size();i+=3) { Glyph g = new Glyph(); g.width=Integer.parseInt(data.get(i)); g.height=Integer.parseInt(data.get(i+1)); String dataArr = data.get(i+2); g.data=new boolean[dataArr.length()]; for (int j=0;j<dataArr.length();j++) { g.data[j]=(dataArr.charAt(j))=='1'?true:false; } if (g.width==0||g.height==0) { System.out.println(" WARNING! Font "+fontName+" does not have glyph data for "+(i/3)+"!"); } font.data[i/3]=g; } } catch (IOException e) { e.printStackTrace(); } return font; } else { try { Files.createFile(f); } catch (IOException e) { e.printStackTrace(); } } return new Font(); } public static void TrainFont(String fontName,String expectedGlyphs,BufferedImage data) { Path f = Paths.get("readers","fonts",fontName); Font font = LoadFont(fontName); int startX=-1; int endX=-1; outer: for (int x=0;x<data.getWidth();x++) { for (int y=0;y<data.getHeight();y++) { if (data.getRGB(x, y)!=TRANSPARENT) { if (startX==-1) { startX=x; } continue outer; } } //If we got to here it means we found transparent on everything. if (startX!=-1) { endX=x; //Create a Glyph out of this. Glyph g = new Glyph(); g.width=endX-startX; g.height=data.getHeight(); int[] arr = data.getRGB(startX,0,g.width,g.height,null,0,g.width); //Find the min and max Y. int startY=g.height; int endY=0; for (int X=0;X<g.width;X++) { for (int Y=0;Y<g.height;Y++) { if (arr[Y*g.width+X]!=TRANSPARENT&&Y<startY) { startY=Y; } if (arr[Y*g.width+X]!=TRANSPARENT&&Y>endY) { endY=Y; } } } g.height=endY-startY; g.data = new boolean[g.width*g.height]; for (int X=0;X<g.width;X++) { for (int Y=0;Y<g.height;Y++) { g.data[Y*g.width+X]=arr[(Y+startY)*g.width+X]!=TRANSPARENT; } } int index = expectedGlyphs.charAt(0)-'0'; if (index>=0&&index<font.data.length) { font.data[index]=g; System.out.println("Glyph for number "+index+" has been saved."); } else { System.out.println("Skip unrecognized glyph: "+expectedGlyphs.charAt(0)); } expectedGlyphs=expectedGlyphs.substring(1); startX=-1; endX=-1; } } saveFont(font,fontName); } private static void saveFont(Font f,String name) { StringBuilder sb = new StringBuilder(); for (int i=0;i<f.data.length;i++) { Glyph g = f.data[i]; if (g==null) { g=new Glyph(); } sb .append(g.width) .append('\n') .append(g.height) .append('\n'); for (int j=0;j<g.data.length;j++) { sb.append(g.data[j]?"1":"0"); } sb.append('\n'); } try { Files.write(Paths.get("readers","fonts",name),sb.toString().getBytes(),StandardOpenOption.TRUNCATE_EXISTING); } catch (IOException e) { e.printStackTrace(); } } public String convertGlyphs(List<Glyph> glyphs) { return convertGlyphs(glyphs,false); } public String convertGlyphs(List<Glyph> glyphs,boolean debug) { StringBuilder sb = new StringBuilder(); for (int i=0;i<glyphs.size();i++) { int bestScore=Integer.MIN_VALUE; int bestGlyph=0; for (int j=0;j<data.length;j++) { Glyph g_a = glyphs.get(i); Glyph g_b = data[j]; int score=0; for (int x=0;x<g_a.width;x++) { for (int y=0;y<g_a.height;y++) { boolean found=false; //We can be up to 1 pixel off and still be valid. inner: for (int xx=-1;xx<=1;xx++) { for (int yy=-1;yy<=1;yy++) { int a_x=x+xx; int a_y=y+yy; if (a_x>=0&&a_x<g_b.width&&a_y>=0&&a_y<g_b.height) { int a_index=a_y*g_b.width+a_x; if (g_a.data[y*g_a.width+x]==g_b.data[a_index]) { found=true; break inner; } } } } if (found) { score++; } else { if (debug) { System.out.print("-"); } score-=2; } } } if (g_b.width-g_a.width>2) { score-=(g_b.width-g_a.width)*g_b.height; } else if (g_b.height-g_a.height>2) { score-=(g_b.height-g_a.height)*g_b.width; } if (score>bestScore) { bestScore=score; bestGlyph=j; if (debug) { System.out.println("Glyph "+j+" has a score of "+bestScore+"."); } } else { if (debug) { System.out.println(" Glyph "+j+" has a score of "+score+"."); } } } sb.append(bestGlyph); } if (debug) { System.out.println(sb.toString()+"\n========"); } return sb.toString(); } }