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