001 package net.minecraft.src; 002 003 import cpw.mods.fml.common.Side; 004 import cpw.mods.fml.common.asm.SideOnly; 005 import java.awt.image.BufferedImage; 006 import java.io.IOException; 007 import java.io.InputStream; 008 import java.text.Bidi; 009 import java.util.Arrays; 010 import java.util.List; 011 import java.util.Random; 012 import javax.imageio.ImageIO; 013 import org.lwjgl.opengl.GL11; 014 015 @SideOnly(Side.CLIENT) 016 public class FontRenderer 017 { 018 /** Array of width of all the characters in default.png */ 019 private int[] charWidth = new int[256]; 020 public int fontTextureName = 0; 021 022 /** the height in pixels of default text */ 023 public int FONT_HEIGHT = 9; 024 public Random fontRandom = new Random(); 025 026 /** 027 * Array of the start/end column (in upper/lower nibble) for every glyph in the /font directory. 028 */ 029 private byte[] glyphWidth = new byte[65536]; 030 031 /** 032 * Array of GL texture ids for loaded glyph_XX.png images. Indexed by Unicode block (group of 256 chars). 033 */ 034 private final int[] glyphTextureName = new int[256]; 035 036 /** 037 * Array of RGB triplets defining the 16 standard chat colors followed by 16 darker version of the same colors for 038 * drop shadows. 039 */ 040 private int[] colorCode = new int[32]; 041 042 /** 043 * The currently bound GL texture ID. Avoids unnecessary glBindTexture() for the same texture if it's already bound. 044 */ 045 private int boundTextureName; 046 047 /** The RenderEngine used to load and setup glyph textures. */ 048 private final RenderEngine renderEngine; 049 050 /** Current X coordinate at which to draw the next character. */ 051 private float posX; 052 053 /** Current Y coordinate at which to draw the next character. */ 054 private float posY; 055 056 /** 057 * If true, strings should be rendered with Unicode fonts instead of the default.png font 058 */ 059 private boolean unicodeFlag; 060 061 /** 062 * If true, the Unicode Bidirectional Algorithm should be run before rendering any string. 063 */ 064 private boolean bidiFlag; 065 066 /** Used to specify new red value for the current color. */ 067 private float red; 068 069 /** Used to specify new blue value for the current color. */ 070 private float blue; 071 072 /** Used to specify new green value for the current color. */ 073 private float green; 074 075 /** Used to speify new alpha value for the current color. */ 076 private float alpha; 077 078 /** Text color of the currently rendering string. */ 079 private int textColor; 080 081 /** Set if the "k" style (random) is active in currently rendering string */ 082 private boolean randomStyle = false; 083 084 /** Set if the "l" style (bold) is active in currently rendering string */ 085 private boolean boldStyle = false; 086 087 /** Set if the "o" style (italic) is active in currently rendering string */ 088 private boolean italicStyle = false; 089 090 /** 091 * Set if the "n" style (underlined) is active in currently rendering string 092 */ 093 private boolean underlineStyle = false; 094 095 /** 096 * Set if the "m" style (strikethrough) is active in currently rendering string 097 */ 098 private boolean strikethroughStyle = false; 099 100 FontRenderer() 101 { 102 this.renderEngine = null; 103 } 104 105 public FontRenderer(GameSettings par1GameSettings, String par2Str, RenderEngine par3RenderEngine, boolean par4) 106 { 107 this.renderEngine = par3RenderEngine; 108 this.unicodeFlag = par4; 109 BufferedImage var5; 110 111 try 112 { 113 var5 = ImageIO.read(RenderEngine.class.getResourceAsStream(par2Str)); 114 InputStream var6 = RenderEngine.class.getResourceAsStream("/font/glyph_sizes.bin"); 115 var6.read(this.glyphWidth); 116 } 117 catch (IOException var18) 118 { 119 throw new RuntimeException(var18); 120 } 121 122 int var19 = var5.getWidth(); 123 int var7 = var5.getHeight(); 124 int[] var8 = new int[var19 * var7]; 125 var5.getRGB(0, 0, var19, var7, var8, 0, var19); 126 int var9 = 0; 127 int var10; 128 int var11; 129 int var12; 130 int var13; 131 int var15; 132 int var16; 133 134 while (var9 < 256) 135 { 136 var10 = var9 % 16; 137 var11 = var9 / 16; 138 var12 = 7; 139 140 while (true) 141 { 142 if (var12 >= 0) 143 { 144 var13 = var10 * 8 + var12; 145 boolean var14 = true; 146 147 for (var15 = 0; var15 < 8 && var14; ++var15) 148 { 149 var16 = (var11 * 8 + var15) * var19; 150 int var17 = var8[var13 + var16] & 255; 151 152 if (var17 > 0) 153 { 154 var14 = false; 155 } 156 } 157 158 if (var14) 159 { 160 --var12; 161 continue; 162 } 163 } 164 165 if (var9 == 32) 166 { 167 var12 = 2; 168 } 169 170 this.charWidth[var9] = var12 + 2; 171 ++var9; 172 break; 173 } 174 } 175 176 this.fontTextureName = par3RenderEngine.allocateAndSetupTexture(var5); 177 178 for (var9 = 0; var9 < 32; ++var9) 179 { 180 var10 = (var9 >> 3 & 1) * 85; 181 var11 = (var9 >> 2 & 1) * 170 + var10; 182 var12 = (var9 >> 1 & 1) * 170 + var10; 183 var13 = (var9 >> 0 & 1) * 170 + var10; 184 185 if (var9 == 6) 186 { 187 var11 += 85; 188 } 189 190 if (par1GameSettings.anaglyph) 191 { 192 int var20 = (var11 * 30 + var12 * 59 + var13 * 11) / 100; 193 var15 = (var11 * 30 + var12 * 70) / 100; 194 var16 = (var11 * 30 + var13 * 70) / 100; 195 var11 = var20; 196 var12 = var15; 197 var13 = var16; 198 } 199 200 if (var9 >= 16) 201 { 202 var11 /= 4; 203 var12 /= 4; 204 var13 /= 4; 205 } 206 207 this.colorCode[var9] = (var11 & 255) << 16 | (var12 & 255) << 8 | var13 & 255; 208 } 209 } 210 211 /** 212 * Pick how to render a single character and return the width used. 213 */ 214 private float renderCharAtPos(int par1, char par2, boolean par3) 215 { 216 return par2 == 32 ? 4.0F : (par1 > 0 && !this.unicodeFlag ? this.renderDefaultChar(par1 + 32, par3) : this.renderUnicodeChar(par2, par3)); 217 } 218 219 /** 220 * Render a single character with the default.png font at current (posX,posY) location... 221 */ 222 private float renderDefaultChar(int par1, boolean par2) 223 { 224 float var3 = (float)(par1 % 16 * 8); 225 float var4 = (float)(par1 / 16 * 8); 226 float var5 = par2 ? 1.0F : 0.0F; 227 228 if (this.boundTextureName != this.fontTextureName) 229 { 230 GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.fontTextureName); 231 this.boundTextureName = this.fontTextureName; 232 } 233 234 float var6 = (float)this.charWidth[par1] - 0.01F; 235 GL11.glBegin(GL11.GL_TRIANGLE_STRIP); 236 GL11.glTexCoord2f(var3 / 128.0F, var4 / 128.0F); 237 GL11.glVertex3f(this.posX + var5, this.posY, 0.0F); 238 GL11.glTexCoord2f(var3 / 128.0F, (var4 + 7.99F) / 128.0F); 239 GL11.glVertex3f(this.posX - var5, this.posY + 7.99F, 0.0F); 240 GL11.glTexCoord2f((var3 + var6) / 128.0F, var4 / 128.0F); 241 GL11.glVertex3f(this.posX + var6 + var5, this.posY, 0.0F); 242 GL11.glTexCoord2f((var3 + var6) / 128.0F, (var4 + 7.99F) / 128.0F); 243 GL11.glVertex3f(this.posX + var6 - var5, this.posY + 7.99F, 0.0F); 244 GL11.glEnd(); 245 return (float)this.charWidth[par1]; 246 } 247 248 /** 249 * Load one of the /font/glyph_XX.png into a new GL texture and store the texture ID in glyphTextureName array. 250 */ 251 private void loadGlyphTexture(int par1) 252 { 253 String var3 = String.format("/font/glyph_%02X.png", new Object[] {Integer.valueOf(par1)}); 254 BufferedImage var2; 255 256 try 257 { 258 var2 = ImageIO.read(RenderEngine.class.getResourceAsStream(var3)); 259 } 260 catch (IOException var5) 261 { 262 throw new RuntimeException(var5); 263 } 264 265 this.glyphTextureName[par1] = this.renderEngine.allocateAndSetupTexture(var2); 266 this.boundTextureName = this.glyphTextureName[par1]; 267 } 268 269 /** 270 * Render a single Unicode character at current (posX,posY) location using one of the /font/glyph_XX.png files... 271 */ 272 private float renderUnicodeChar(char par1, boolean par2) 273 { 274 if (this.glyphWidth[par1] == 0) 275 { 276 return 0.0F; 277 } 278 else 279 { 280 int var3 = par1 / 256; 281 282 if (this.glyphTextureName[var3] == 0) 283 { 284 this.loadGlyphTexture(var3); 285 } 286 287 if (this.boundTextureName != this.glyphTextureName[var3]) 288 { 289 GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.glyphTextureName[var3]); 290 this.boundTextureName = this.glyphTextureName[var3]; 291 } 292 293 int var4 = this.glyphWidth[par1] >>> 4; 294 int var5 = this.glyphWidth[par1] & 15; 295 float var6 = (float)var4; 296 float var7 = (float)(var5 + 1); 297 float var8 = (float)(par1 % 16 * 16) + var6; 298 float var9 = (float)((par1 & 255) / 16 * 16); 299 float var10 = var7 - var6 - 0.02F; 300 float var11 = par2 ? 1.0F : 0.0F; 301 GL11.glBegin(GL11.GL_TRIANGLE_STRIP); 302 GL11.glTexCoord2f(var8 / 256.0F, var9 / 256.0F); 303 GL11.glVertex3f(this.posX + var11, this.posY, 0.0F); 304 GL11.glTexCoord2f(var8 / 256.0F, (var9 + 15.98F) / 256.0F); 305 GL11.glVertex3f(this.posX - var11, this.posY + 7.99F, 0.0F); 306 GL11.glTexCoord2f((var8 + var10) / 256.0F, var9 / 256.0F); 307 GL11.glVertex3f(this.posX + var10 / 2.0F + var11, this.posY, 0.0F); 308 GL11.glTexCoord2f((var8 + var10) / 256.0F, (var9 + 15.98F) / 256.0F); 309 GL11.glVertex3f(this.posX + var10 / 2.0F - var11, this.posY + 7.99F, 0.0F); 310 GL11.glEnd(); 311 return (var7 - var6) / 2.0F + 1.0F; 312 } 313 } 314 315 /** 316 * Draws the specified string with a shadow. 317 */ 318 public int drawStringWithShadow(String par1Str, int par2, int par3, int par4) 319 { 320 this.resetStyles(); 321 322 if (this.bidiFlag) 323 { 324 par1Str = this.bidiReorder(par1Str); 325 } 326 327 int var5 = this.renderString(par1Str, par2 + 1, par3 + 1, par4, true); 328 var5 = Math.max(var5, this.renderString(par1Str, par2, par3, par4, false)); 329 return var5; 330 } 331 332 /** 333 * Draws the specified string. 334 */ 335 public void drawString(String par1Str, int par2, int par3, int par4) 336 { 337 this.resetStyles(); 338 339 if (this.bidiFlag) 340 { 341 par1Str = this.bidiReorder(par1Str); 342 } 343 344 this.renderString(par1Str, par2, par3, par4, false); 345 } 346 347 /** 348 * Apply Unicode Bidirectional Algorithm to string and return a new possibly reordered string for visual rendering. 349 */ 350 private String bidiReorder(String par1Str) 351 { 352 if (par1Str != null && Bidi.requiresBidi(par1Str.toCharArray(), 0, par1Str.length())) 353 { 354 Bidi var2 = new Bidi(par1Str, -2); 355 byte[] var3 = new byte[var2.getRunCount()]; 356 String[] var4 = new String[var3.length]; 357 int var7; 358 359 for (int var5 = 0; var5 < var3.length; ++var5) 360 { 361 int var6 = var2.getRunStart(var5); 362 var7 = var2.getRunLimit(var5); 363 int var8 = var2.getRunLevel(var5); 364 String var9 = par1Str.substring(var6, var7); 365 var3[var5] = (byte)var8; 366 var4[var5] = var9; 367 } 368 369 String[] var11 = (String[])var4.clone(); 370 Bidi.reorderVisually(var3, 0, var4, 0, var3.length); 371 StringBuilder var12 = new StringBuilder(); 372 var7 = 0; 373 374 while (var7 < var4.length) 375 { 376 byte var13 = var3[var7]; 377 int var14 = 0; 378 379 while (true) 380 { 381 if (var14 < var11.length) 382 { 383 if (!var11[var14].equals(var4[var7])) 384 { 385 ++var14; 386 continue; 387 } 388 389 var13 = var3[var14]; 390 } 391 392 if ((var13 & 1) == 0) 393 { 394 var12.append(var4[var7]); 395 } 396 else 397 { 398 for (var14 = var4[var7].length() - 1; var14 >= 0; --var14) 399 { 400 char var10 = var4[var7].charAt(var14); 401 402 if (var10 == 40) 403 { 404 var10 = 41; 405 } 406 else if (var10 == 41) 407 { 408 var10 = 40; 409 } 410 411 var12.append(var10); 412 } 413 } 414 415 ++var7; 416 break; 417 } 418 } 419 420 return var12.toString(); 421 } 422 else 423 { 424 return par1Str; 425 } 426 } 427 428 /** 429 * Reset all style flag fields in the class to false; called at the start of string rendering 430 */ 431 private void resetStyles() 432 { 433 this.randomStyle = false; 434 this.boldStyle = false; 435 this.italicStyle = false; 436 this.underlineStyle = false; 437 this.strikethroughStyle = false; 438 } 439 440 /** 441 * Render a single line string at the current (posX,posY) and update posX 442 */ 443 private void renderStringAtPos(String par1Str, boolean par2) 444 { 445 for (int var3 = 0; var3 < par1Str.length(); ++var3) 446 { 447 char var4 = par1Str.charAt(var3); 448 int var5; 449 int var6; 450 451 if (var4 == 167 && var3 + 1 < par1Str.length()) 452 { 453 var5 = "0123456789abcdefklmnor".indexOf(par1Str.toLowerCase().charAt(var3 + 1)); 454 455 if (var5 < 16) 456 { 457 this.randomStyle = false; 458 this.boldStyle = false; 459 this.strikethroughStyle = false; 460 this.underlineStyle = false; 461 this.italicStyle = false; 462 463 if (var5 < 0 || var5 > 15) 464 { 465 var5 = 15; 466 } 467 468 if (par2) 469 { 470 var5 += 16; 471 } 472 473 var6 = this.colorCode[var5]; 474 this.textColor = var6; 475 GL11.glColor4f((float)(var6 >> 16) / 255.0F, (float)(var6 >> 8 & 255) / 255.0F, (float)(var6 & 255) / 255.0F, this.alpha); 476 } 477 else if (var5 == 16) 478 { 479 this.randomStyle = true; 480 } 481 else if (var5 == 17) 482 { 483 this.boldStyle = true; 484 } 485 else if (var5 == 18) 486 { 487 this.strikethroughStyle = true; 488 } 489 else if (var5 == 19) 490 { 491 this.underlineStyle = true; 492 } 493 else if (var5 == 20) 494 { 495 this.italicStyle = true; 496 } 497 else if (var5 == 21) 498 { 499 this.randomStyle = false; 500 this.boldStyle = false; 501 this.strikethroughStyle = false; 502 this.underlineStyle = false; 503 this.italicStyle = false; 504 GL11.glColor4f(this.red, this.blue, this.green, this.alpha); 505 } 506 507 ++var3; 508 } 509 else 510 { 511 var5 = ChatAllowedCharacters.allowedCharacters.indexOf(var4); 512 513 if (this.randomStyle && var5 > 0) 514 { 515 do 516 { 517 var6 = this.fontRandom.nextInt(ChatAllowedCharacters.allowedCharacters.length()); 518 } 519 while (this.charWidth[var5 + 32] != this.charWidth[var6 + 32]); 520 521 var5 = var6; 522 } 523 524 float var9 = this.renderCharAtPos(var5, var4, this.italicStyle); 525 526 if (this.boldStyle) 527 { 528 ++this.posX; 529 this.renderCharAtPos(var5, var4, this.italicStyle); 530 --this.posX; 531 ++var9; 532 } 533 534 Tessellator var7; 535 536 if (this.strikethroughStyle) 537 { 538 var7 = Tessellator.instance; 539 GL11.glDisable(GL11.GL_TEXTURE_2D); 540 var7.startDrawingQuads(); 541 var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D); 542 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2)), 0.0D); 543 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D); 544 var7.addVertex((double)this.posX, (double)(this.posY + (float)(this.FONT_HEIGHT / 2) - 1.0F), 0.0D); 545 var7.draw(); 546 GL11.glEnable(GL11.GL_TEXTURE_2D); 547 } 548 549 if (this.underlineStyle) 550 { 551 var7 = Tessellator.instance; 552 GL11.glDisable(GL11.GL_TEXTURE_2D); 553 var7.startDrawingQuads(); 554 int var8 = this.underlineStyle ? -1 : 0; 555 var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D); 556 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT), 0.0D); 557 var7.addVertex((double)(this.posX + var9), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D); 558 var7.addVertex((double)(this.posX + (float)var8), (double)(this.posY + (float)this.FONT_HEIGHT - 1.0F), 0.0D); 559 var7.draw(); 560 GL11.glEnable(GL11.GL_TEXTURE_2D); 561 } 562 563 this.posX += (float)((int)var9); 564 } 565 } 566 } 567 568 /** 569 * Render string either left or right aligned depending on bidiFlag 570 */ 571 private int renderStringAligned(String par1Str, int par2, int par3, int par4, int par5, boolean par6) 572 { 573 if (this.bidiFlag) 574 { 575 par1Str = this.bidiReorder(par1Str); 576 int var7 = this.getStringWidth(par1Str); 577 par2 = par2 + par4 - var7; 578 } 579 580 return this.renderString(par1Str, par2, par3, par5, par6); 581 } 582 583 /** 584 * Render single line string by setting GL color, current (posX,posY), and calling renderStringAtPos() 585 */ 586 private int renderString(String par1Str, int par2, int par3, int par4, boolean par5) 587 { 588 if (par1Str != null) 589 { 590 this.boundTextureName = 0; 591 592 if ((par4 & -67108864) == 0) 593 { 594 par4 |= -16777216; 595 } 596 597 if (par5) 598 { 599 par4 = (par4 & 16579836) >> 2 | par4 & -16777216; 600 } 601 602 this.red = (float)(par4 >> 16 & 255) / 255.0F; 603 this.blue = (float)(par4 >> 8 & 255) / 255.0F; 604 this.green = (float)(par4 & 255) / 255.0F; 605 this.alpha = (float)(par4 >> 24 & 255) / 255.0F; 606 GL11.glColor4f(this.red, this.blue, this.green, this.alpha); 607 this.posX = (float)par2; 608 this.posY = (float)par3; 609 this.renderStringAtPos(par1Str, par5); 610 return (int)this.posX; 611 } 612 else 613 { 614 return 0; 615 } 616 } 617 618 /** 619 * Returns the width of this string. Equivalent of FontMetrics.stringWidth(String s). 620 */ 621 public int getStringWidth(String par1Str) 622 { 623 if (par1Str == null) 624 { 625 return 0; 626 } 627 else 628 { 629 int var2 = 0; 630 boolean var3 = false; 631 632 for (int var4 = 0; var4 < par1Str.length(); ++var4) 633 { 634 char var5 = par1Str.charAt(var4); 635 int var6 = this.getCharWidth(var5); 636 637 if (var6 < 0 && var4 < par1Str.length() - 1) 638 { 639 ++var4; 640 var5 = par1Str.charAt(var4); 641 642 if (var5 != 108 && var5 != 76) 643 { 644 if (var5 == 114 || var5 == 82) 645 { 646 var3 = false; 647 } 648 } 649 else 650 { 651 var3 = true; 652 } 653 } 654 655 var2 += var6; 656 657 if (var3) 658 { 659 ++var2; 660 } 661 } 662 663 return var2; 664 } 665 } 666 667 /** 668 * Returns the width of this character as rendered. 669 */ 670 public int getCharWidth(char par1) 671 { 672 if (par1 == 167) 673 { 674 return -1; 675 } 676 else if (par1 == 32) 677 { 678 return 4; 679 } 680 else 681 { 682 int var2 = ChatAllowedCharacters.allowedCharacters.indexOf(par1); 683 684 if (var2 >= 0 && !this.unicodeFlag) 685 { 686 return this.charWidth[var2 + 32]; 687 } 688 else if (this.glyphWidth[par1] != 0) 689 { 690 int var3 = this.glyphWidth[par1] >>> 4; 691 int var4 = this.glyphWidth[par1] & 15; 692 693 if (var4 > 7) 694 { 695 var4 = 15; 696 var3 = 0; 697 } 698 699 ++var4; 700 return (var4 - var3) / 2 + 1; 701 } 702 else 703 { 704 return 0; 705 } 706 } 707 } 708 709 /** 710 * Trims a string to fit a specified Width. 711 */ 712 public String trimStringToWidth(String par1Str, int par2) 713 { 714 return this.trimStringToWidth(par1Str, par2, false); 715 } 716 717 /** 718 * Trims a string to a specified width, and will reverse it if par3 is set. 719 */ 720 public String trimStringToWidth(String par1Str, int par2, boolean par3) 721 { 722 StringBuilder var4 = new StringBuilder(); 723 int var5 = 0; 724 int var6 = par3 ? par1Str.length() - 1 : 0; 725 int var7 = par3 ? -1 : 1; 726 boolean var8 = false; 727 boolean var9 = false; 728 729 for (int var10 = var6; var10 >= 0 && var10 < par1Str.length() && var5 < par2; var10 += var7) 730 { 731 char var11 = par1Str.charAt(var10); 732 int var12 = this.getCharWidth(var11); 733 734 if (var8) 735 { 736 var8 = false; 737 738 if (var11 != 108 && var11 != 76) 739 { 740 if (var11 == 114 || var11 == 82) 741 { 742 var9 = false; 743 } 744 } 745 else 746 { 747 var9 = true; 748 } 749 } 750 else if (var12 < 0) 751 { 752 var8 = true; 753 } 754 else 755 { 756 var5 += var12; 757 758 if (var9) 759 { 760 ++var5; 761 } 762 } 763 764 if (var5 > par2) 765 { 766 break; 767 } 768 769 if (par3) 770 { 771 var4.insert(0, var11); 772 } 773 else 774 { 775 var4.append(var11); 776 } 777 } 778 779 return var4.toString(); 780 } 781 782 /** 783 * Remove all newline characters from the end of the string 784 */ 785 private String trimStringNewline(String par1Str) 786 { 787 while (par1Str != null && par1Str.endsWith("\n")) 788 { 789 par1Str = par1Str.substring(0, par1Str.length() - 1); 790 } 791 792 return par1Str; 793 } 794 795 /** 796 * Splits and draws a String with wordwrap (maximum length is parameter k) 797 */ 798 public void drawSplitString(String par1Str, int par2, int par3, int par4, int par5) 799 { 800 this.resetStyles(); 801 this.textColor = par5; 802 par1Str = this.trimStringNewline(par1Str); 803 this.renderSplitStringNoShadow(par1Str, par2, par3, par4, par5); 804 } 805 806 /** 807 * renders a multi-line string with wordwrap (maximum length is parameter k) by means of renderSplitString 808 */ 809 private void renderSplitStringNoShadow(String par1Str, int par2, int par3, int par4, int par5) 810 { 811 this.textColor = par5; 812 this.renderSplitString(par1Str, par2, par3, par4, false); 813 } 814 815 /** 816 * Perform actual work of rendering a multi-line string with wordwrap and with darker drop shadow color if flag is 817 * set 818 */ 819 private void renderSplitString(String par1Str, int par2, int par3, int par4, boolean par5) 820 { 821 String[] var6 = par1Str.split("\n"); 822 823 if (var6.length > 1) 824 { 825 boolean var12 = false; 826 String[] var13 = var6; 827 int var14 = var6.length; 828 829 for (int var15 = 0; var15 < var14; ++var15) 830 { 831 String var16 = var13[var15]; 832 833 if (var12) 834 { 835 var16 = "\u00a7" + var16; 836 var12 = false; 837 } 838 839 if (var16.endsWith("\u00a7")) 840 { 841 var12 = true; 842 var16 = var16.substring(0, var16.length() - 1); 843 } 844 845 this.renderSplitString(var16, par2, par3, par4, par5); 846 par3 += this.splitStringWidth(var16, par4); 847 } 848 } 849 else 850 { 851 String[] var7 = par1Str.split(" "); 852 int var8 = 0; 853 String var9; 854 855 for (var9 = ""; var8 < var7.length; ++var8) 856 { 857 String var10 = var7[var8]; 858 859 if (this.getStringWidth(var10) >= par4) 860 { 861 if (var9.length() > 0) 862 { 863 this.renderStringAligned(var9, par2, par3, par4, this.textColor, par5); 864 par3 += this.FONT_HEIGHT; 865 } 866 867 do 868 { 869 int var11; 870 871 for (var11 = 1; this.getStringWidth(var10.substring(0, var11)) < par4; ++var11) 872 { 873 ; 874 } 875 876 this.renderStringAligned(var10.substring(0, var11 - 1), par2, par3, par4, this.textColor, par5); 877 par3 += this.FONT_HEIGHT; 878 var10 = var10.substring(var11 - 1); 879 } 880 while (this.getStringWidth(var10) >= par4); 881 882 var9 = var10; 883 } 884 else if (this.getStringWidth(var9 + " " + var10) >= par4) 885 { 886 this.renderStringAligned(var9, par2, par3, par4, this.textColor, par5); 887 par3 += this.FONT_HEIGHT; 888 var9 = var10; 889 } 890 else 891 { 892 if (var9.length() > 0) 893 { 894 var9 = var9 + " "; 895 } 896 897 var9 = var9 + var10; 898 } 899 } 900 901 this.renderStringAligned(var9, par2, par3, par4, this.textColor, par5); 902 } 903 } 904 905 /** 906 * Returns the width of the wordwrapped String (maximum length is parameter k) 907 */ 908 public int splitStringWidth(String par1Str, int par2) 909 { 910 String[] var3 = par1Str.split("\n"); 911 int var6; 912 String var8; 913 914 if (var3.length > 1) 915 { 916 int var10 = 0; 917 String[] var11 = var3; 918 var6 = var3.length; 919 920 for (int var12 = 0; var12 < var6; ++var12) 921 { 922 var8 = var11[var12]; 923 var10 += this.splitStringWidth(var8, par2); 924 } 925 926 return var10; 927 } 928 else 929 { 930 String[] var4 = par1Str.split(" "); 931 int var5 = 0; 932 var6 = 0; 933 String var7; 934 935 for (var7 = ""; var6 < var4.length; ++var6) 936 { 937 var8 = var4[var6]; 938 939 if (this.getStringWidth(var8) >= par2) 940 { 941 if (var7.length() > 0) 942 { 943 var5 += this.FONT_HEIGHT; 944 } 945 946 do 947 { 948 int var9; 949 950 for (var9 = 1; this.getStringWidth(var8.substring(0, var9)) < par2; ++var9) 951 { 952 ; 953 } 954 955 var5 += this.FONT_HEIGHT; 956 var8 = var8.substring(var9 - 1); 957 } 958 while (this.getStringWidth(var8) >= par2); 959 960 var7 = var8; 961 } 962 else if (this.getStringWidth(var7 + " " + var8) >= par2) 963 { 964 var5 += this.FONT_HEIGHT; 965 var7 = var8; 966 } 967 else 968 { 969 if (var7.length() > 0) 970 { 971 var7 = var7 + " "; 972 } 973 974 var7 = var7 + var8; 975 } 976 } 977 978 if (var7.length() > 0) 979 { 980 var5 += this.FONT_HEIGHT; 981 } 982 983 return var5; 984 } 985 } 986 987 /** 988 * Set unicodeFlag controlling whether strings should be rendered with Unicode fonts instead of the default.png 989 * font. 990 */ 991 public void setUnicodeFlag(boolean par1) 992 { 993 this.unicodeFlag = par1; 994 } 995 996 public boolean func_82883_a() 997 { 998 return this.unicodeFlag; 999 } 1000 1001 /** 1002 * Set bidiFlag to control if the Unicode Bidirectional Algorithm should be run before rendering any string. 1003 */ 1004 public void setBidiFlag(boolean par1) 1005 { 1006 this.bidiFlag = par1; 1007 } 1008 1009 /** 1010 * Breaks a string into a list of pieces that will fit a specified width. 1011 */ 1012 public List listFormattedStringToWidth(String par1Str, int par2) 1013 { 1014 return Arrays.asList(this.wrapFormattedStringToWidth(par1Str, par2).split("\n")); 1015 } 1016 1017 /** 1018 * Inserts newline and formatting into a string to wrap it within the specified width. 1019 */ 1020 String wrapFormattedStringToWidth(String par1Str, int par2) 1021 { 1022 int var3 = this.sizeStringToWidth(par1Str, par2); 1023 1024 if (par1Str.length() <= var3) 1025 { 1026 return par1Str; 1027 } 1028 else 1029 { 1030 String var4 = par1Str.substring(0, var3); 1031 String var5 = getFormatFromString(var4) + par1Str.substring(var3 + (par1Str.charAt(var3) == 32 ? 1 : 0)); 1032 return var4 + "\n" + this.wrapFormattedStringToWidth(var5, par2); 1033 } 1034 } 1035 1036 /** 1037 * Determines how many characters from the string will fit into the specified width. 1038 */ 1039 private int sizeStringToWidth(String par1Str, int par2) 1040 { 1041 int var3 = par1Str.length(); 1042 int var4 = 0; 1043 int var5 = 0; 1044 int var6 = -1; 1045 1046 for (boolean var7 = false; var5 < var3; ++var5) 1047 { 1048 char var8 = par1Str.charAt(var5); 1049 1050 switch (var8) 1051 { 1052 case 167: 1053 if (var5 < var3 - 1) 1054 { 1055 ++var5; 1056 char var9 = par1Str.charAt(var5); 1057 1058 if (var9 != 108 && var9 != 76) 1059 { 1060 if (var9 == 114 || var9 == 82) 1061 { 1062 var7 = false; 1063 } 1064 } 1065 else 1066 { 1067 var7 = true; 1068 } 1069 } 1070 1071 break; 1072 case 32: 1073 var6 = var5; 1074 default: 1075 var4 += this.getCharWidth(var8); 1076 1077 if (var7) 1078 { 1079 ++var4; 1080 } 1081 } 1082 1083 if (var8 == 10) 1084 { 1085 ++var5; 1086 var6 = var5; 1087 break; 1088 } 1089 1090 if (var4 > par2) 1091 { 1092 break; 1093 } 1094 } 1095 1096 return var5 != var3 && var6 != -1 && var6 < var5 ? var6 : var5; 1097 } 1098 1099 /** 1100 * Checks if the char code is a hexadecimal character, used to set colour. 1101 */ 1102 private static boolean isFormatColor(char par0) 1103 { 1104 return par0 >= 48 && par0 <= 57 || par0 >= 97 && par0 <= 102 || par0 >= 65 && par0 <= 70; 1105 } 1106 1107 /** 1108 * Checks if the char code is O-K...lLrRk-o... used to set special formatting. 1109 */ 1110 private static boolean isFormatSpecial(char par0) 1111 { 1112 return par0 >= 107 && par0 <= 111 || par0 >= 75 && par0 <= 79 || par0 == 114 || par0 == 82; 1113 } 1114 1115 /** 1116 * Digests a string for nonprinting formatting characters then returns a string containing only that formatting. 1117 */ 1118 private static String getFormatFromString(String par0Str) 1119 { 1120 String var1 = ""; 1121 int var2 = -1; 1122 int var3 = par0Str.length(); 1123 1124 while ((var2 = par0Str.indexOf(167, var2 + 1)) != -1) 1125 { 1126 if (var2 < var3 - 1) 1127 { 1128 char var4 = par0Str.charAt(var2 + 1); 1129 1130 if (isFormatColor(var4)) 1131 { 1132 var1 = "\u00a7" + var4; 1133 } 1134 else if (isFormatSpecial(var4)) 1135 { 1136 var1 = var1 + "\u00a7" + var4; 1137 } 1138 } 1139 } 1140 1141 return var1; 1142 } 1143 1144 /** 1145 * Get bidiFlag that controls if the Unicode Bidirectional Algorithm should be run before rendering any string 1146 */ 1147 public boolean getBidiFlag() 1148 { 1149 return this.bidiFlag; 1150 } 1151 }