├── src ├── com │ └── badlogic │ │ └── gdx │ │ └── graphics │ │ └── g2d │ │ └── freetype │ │ ├── gwt │ │ ├── inject │ │ │ ├── OnCompletion.java │ │ │ └── JsInjector.java │ │ ├── FreetypeInjector.java │ │ └── emu │ │ │ └── com │ │ │ └── badlogic │ │ │ └── gdx │ │ │ └── graphics │ │ │ ├── g2d │ │ │ └── freetype │ │ │ │ ├── FreeTypeFontGeneratorLoader.java │ │ │ │ ├── FreetypeFontLoader.java │ │ │ │ ├── FreeTypeFontGenerator.java │ │ │ │ └── FreeType.java │ │ │ └── FreeTypePixmap.java │ │ └── freetype-gwt.gwt.xml └── java │ ├── nio.gwt.xml │ └── nio │ └── FreeTypeUtil.java ├── .settings └── org.eclipse.core.resources.prefs ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── .gitignore ├── .classpath ├── .project ├── README.md ├── deploy.gradle ├── gradlew.bat ├── gradlew └── LICENSE /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/inject/OnCompletion.java: -------------------------------------------------------------------------------- 1 | 2 | package com.badlogic.gdx.graphics.g2d.freetype.gwt.inject; 3 | 4 | public interface OnCompletion { 5 | 6 | void run (); 7 | } 8 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/com/badlogic/gdx/graphics/g2d/freetype/FreeType.java=UTF-8 3 | encoding//src/com/badlogic/gdx/graphics/g2d/freetype/FreeTypeFontGenerator.java=UTF-8 4 | -------------------------------------------------------------------------------- /src/java/nio.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 20 22:02:10 CEST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-bin.zip 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | /bin/ 14 | /build/ 15 | /.gradle/ 16 | /.settings/ 17 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/freetype-gwt.gwt.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/java/nio/FreeTypeUtil.java: -------------------------------------------------------------------------------- 1 | 2 | package java.nio; 3 | 4 | import com.google.gwt.typedarrays.shared.ArrayBuffer; 5 | 6 | public class FreeTypeUtil { 7 | 8 | public static DirectReadWriteByteBuffer newDirectReadWriteByteBuffer (ArrayBuffer backingArray) { 9 | return new DirectReadWriteByteBuffer(backingArray); 10 | } 11 | 12 | public static DirectReadWriteByteBuffer newDirectReadWriteByteBuffer (ArrayBuffer backingArray, int capacity, 13 | int arrayOffset) { 14 | return new DirectReadWriteByteBuffer(backingArray, capacity, arrayOffset); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | gdx-freetype-gwt 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | com.google.gdt.eclipse.core.webAppProjectValidator 15 | 16 | 17 | 18 | 19 | com.google.gwt.eclipse.core.gwtProjectValidator 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.jdt.core.javanature 26 | com.google.gwt.eclipse.core.gwtNature 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/inject/JsInjector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package com.badlogic.gdx.graphics.g2d.freetype.gwt.inject; 18 | 19 | /** Stub interface, here to trigger the {@link JsInjectorGenerator} so we can get all .js files and inject them 20 | * @author Simon Gerst */ 21 | public interface JsInjector { 22 | /** @return returns all injectables i.e. classes that implement {@link Injectable} */ 23 | Injectable[] getInjectables (); 24 | 25 | /** Represents a .js script that needs to be injected, it is ensured that the script is injected. See {@link FreetypeInjector} 26 | * in gdx-freetype-gwt for an example. You can only use GWT in those methods, you can not use libgdx! */ 27 | public static interface Injectable { 28 | /** @return return true if the injection succeeded */ 29 | boolean isSuccess (); 30 | 31 | /** @return return true if the injection failed */ 32 | boolean isError (); 33 | 34 | /** This method will be called by GWT and will inject the js you specified */ 35 | void inject (OnCompletion oc); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gdx-freetype-gwt 2 | 3 | You ever wanted to use freetype on the web version of your game but couldn't? Now you can! 4 | 5 | # Versions 6 | You have to use a matching libGDX and gdx-freetype-gwt version. 7 | See this table for compatibility. 8 | | libGDX | gdx-freetype-gwt | Note | 9 | |:--------:|:----------------:|:------:| 10 | | 1.9.11-SNAPSHOT | 1.9.11-SNAPSHOT | | 11 | | 1.9.10 | 1.9.10.1 | Fixes https://github.com/intrigus/gdx-freetype-gwt/issues/9 | 12 | | 1.9.10 | 1.9.10 | Don't use. Use 1.9.10.1 instead | 13 | 14 | # How-To 15 | 1. Go to your `GdxDefinition.gwt.xml` in your `html` subproject 16 | 17 | Add 18 | 19 | `` 20 | 21 | after 22 | 23 | `` 24 | 25 | 2. Change your `build.gradle` of the `html` subproject 26 | 27 | Add 28 | ```` 29 | implementation "com.github.intrigus.gdx-freetype-gwt:gdx-freetype-gwt:$version" 30 | implementation "com.github.intrigus.gdx-freetype-gwt:gdx-freetype-gwt:$version:sources" 31 | ```` 32 | 33 | 3. Modify your `HtmlLauncher.java` (or if it's not named so, modify the class in your `html` project that extends `GwtApplication`) 34 | 35 | Add 36 | ````java 37 | @Override 38 | public void onModuleLoad () { 39 | FreetypeInjector.inject(new OnCompletion() { 40 | public void run () { 41 | // Replace HtmlLauncher with the class name 42 | // If your class is called FooBar.java than the line should be FooBar.super.onModuleLoad(); 43 | HtmlLauncher.super.onModuleLoad(); 44 | } 45 | }); 46 | } 47 | ```` 48 | 49 | 4. Profit and Enjoy 50 | 51 | # Note 52 | If gradle fails to resolve the dependency this most likely means that there no matching gdx-freetype-gwt version has been published. 53 | 54 | You should try previous versions since freetype is generally updated rarely. 55 | 56 | In any case open an issue. 57 | -------------------------------------------------------------------------------- /deploy.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | apply plugin: 'signing' 3 | 4 | ext.isReleaseVersion = !version.endsWith("SNAPSHOT") 5 | 6 | task javadocJar(type: Jar) { 7 | classifier = 'javadoc' 8 | from javadoc 9 | } 10 | 11 | task sourcesJar(type: Jar) { 12 | classifier = 'sources' 13 | from sourceSets.main.allSource 14 | } 15 | 16 | artifacts { 17 | archives javadocJar, sourcesJar 18 | } 19 | 20 | javadoc { 21 | onlyIf { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") } 22 | options.encoding = 'UTF-8' 23 | options.addStringOption('Xdoclint:none', '-quiet') 24 | } 25 | 26 | signing { 27 | required { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") } 28 | sign configurations.archives 29 | } 30 | 31 | uploadArchives { 32 | repositories { 33 | mavenDeployer { 34 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 35 | 36 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 37 | authentication(userName: ossrhUsername, password: ossrhPassword) 38 | } 39 | 40 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 41 | authentication(userName: ossrhUsername, password: ossrhPassword) 42 | } 43 | 44 | pom.project { 45 | url 'https://github.com/intrigus/gdx-freetype-gwt/' 46 | 47 | scm { 48 | connection 'scm:git:git@github.com:intrigus/gdx-freetype-gwt.git' 49 | developerConnection 'scm:git:git@github.com:intrigus/gdx-freetype-gwt.git' 50 | url 'git@github.com:intrigus/gdx-freetype-gwt.git' 51 | } 52 | 53 | licenses { 54 | license { 55 | name 'The Apache License, Version 2.0' 56 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 57 | } 58 | } 59 | 60 | developers { 61 | developer { 62 | id 'intrigus' 63 | name 'Simon Gerst' 64 | url 'https://github.com/intrigus' 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/FreetypeInjector.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package com.badlogic.gdx.graphics.g2d.freetype.gwt; 18 | 19 | import com.badlogic.gdx.graphics.g2d.freetype.gwt.inject.JsInjector.Injectable; 20 | import com.badlogic.gdx.graphics.g2d.freetype.gwt.inject.OnCompletion; 21 | import com.google.gwt.core.client.Callback; 22 | import com.google.gwt.core.client.GWT; 23 | import com.google.gwt.core.client.ScriptInjector; 24 | 25 | public class FreetypeInjector { 26 | private static final FreetypeInjector_ instance = new FreetypeInjector_(); 27 | 28 | public static void inject (OnCompletion oc) { 29 | instance.inject(oc); 30 | } 31 | 32 | private static class FreetypeInjector_ implements Injectable { 33 | 34 | private boolean success; 35 | private boolean error; 36 | 37 | @Override 38 | public void inject (final OnCompletion oc) { 39 | final String js = GWT.getModuleBaseForStaticFiles() + "freetype.js"; 40 | ScriptInjector.fromUrl(js).setCallback(new Callback() { 41 | 42 | @Override 43 | public void onFailure (Exception reason) { 44 | error = true; 45 | GWT.log("Exception injecting " + js, reason); 46 | oc.run(); 47 | } 48 | 49 | @Override 50 | public void onSuccess (Void result) { 51 | success = true; 52 | GWT.log("Success injecting js."); 53 | oc.run(); 54 | } 55 | 56 | }).setWindow(ScriptInjector.TOP_WINDOW).inject(); 57 | } 58 | 59 | @Override 60 | public boolean isSuccess () { 61 | return success; 62 | } 63 | 64 | @Override 65 | public boolean isError () { 66 | return error; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/emu/com/badlogic/gdx/graphics/g2d/freetype/FreeTypeFontGeneratorLoader.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package com.badlogic.gdx.graphics.g2d.freetype; 18 | 19 | import com.badlogic.gdx.assets.AssetDescriptor; 20 | import com.badlogic.gdx.assets.AssetLoaderParameters; 21 | import com.badlogic.gdx.assets.AssetManager; 22 | import com.badlogic.gdx.assets.loaders.FileHandleResolver; 23 | import com.badlogic.gdx.assets.loaders.SynchronousAssetLoader; 24 | import com.badlogic.gdx.files.FileHandle; 25 | import com.badlogic.gdx.utils.Array; 26 | 27 | /** Makes {@link FreeTypeFontGenerator} managable via {@link AssetManager}. 28 | *

29 | * Do 30 | * {@code assetManager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(new InternalFileHandleResolver()))} 31 | * to register it. 32 | *

33 | * @author Daniel Holderbaum */ 34 | public class FreeTypeFontGeneratorLoader extends 35 | SynchronousAssetLoader { 36 | 37 | public FreeTypeFontGeneratorLoader (FileHandleResolver resolver) { 38 | super(resolver); 39 | } 40 | 41 | @Override 42 | public FreeTypeFontGenerator load (AssetManager assetManager, String fileName, FileHandle file, 43 | FreeTypeFontGeneratorParameters parameter) { 44 | FreeTypeFontGenerator generator = null; 45 | if (file.extension().equals("gen")) { 46 | generator = new FreeTypeFontGenerator(file.sibling(file.nameWithoutExtension())); 47 | } else { 48 | generator = new FreeTypeFontGenerator(file); 49 | } 50 | return generator; 51 | } 52 | 53 | @Override 54 | public Array getDependencies (String fileName, FileHandle file, FreeTypeFontGeneratorParameters parameter) { 55 | return null; 56 | } 57 | 58 | static public class FreeTypeFontGeneratorParameters extends AssetLoaderParameters { 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/emu/com/badlogic/gdx/graphics/FreeTypePixmap.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package com.badlogic.gdx.graphics; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.nio.DirectReadWriteByteBuffer; 21 | import java.nio.FreeTypeUtil; 22 | import java.nio.HasArrayBufferView; 23 | 24 | import com.google.gwt.canvas.dom.client.Context2d; 25 | import com.google.gwt.core.client.GWT; 26 | import com.google.gwt.typedarrays.shared.ArrayBufferView; 27 | import com.google.gwt.typedarrays.shared.Uint8ClampedArray; 28 | 29 | /** @author Simon Gerst */ 30 | public class FreeTypePixmap extends Pixmap { 31 | 32 | ByteBuffer buffer; 33 | 34 | public FreeTypePixmap (int width, int height, Format format) { 35 | super(width, height, format); 36 | } 37 | 38 | public void setPixelsNull () { 39 | pixels = null; 40 | } 41 | 42 | public ByteBuffer getRealPixels () { 43 | if (getWidth() == 0 || getHeight() == 0) { 44 | return new DirectReadWriteByteBuffer(0); 45 | } 46 | if (pixels == null) { 47 | pixels = getContext().getImageData(0, 0, getWidth(), getHeight()).getData(); 48 | buffer = FreeTypeUtil.newDirectReadWriteByteBuffer(((Uint8ClampedArray)pixels).buffer()); 49 | return buffer; 50 | } 51 | return buffer; 52 | } 53 | 54 | public void putPixelsBack (ByteBuffer pixels) { 55 | if (getWidth() == 0 || getHeight() == 0) return; 56 | putPixelsBack(((HasArrayBufferView)pixels).getTypedArray(), getWidth(), getHeight(), getContext()); 57 | 58 | } 59 | 60 | private native void putPixelsBack (ArrayBufferView pixels, int width, int height, Context2d ctx)/*-{ 61 | var imgData = ctx.createImageData(width, height); 62 | var data = imgData.data; 63 | for (var i = 0, len = width * height * 4; i < len; i++) { 64 | data[i] = pixels[i] & 0xff; 65 | } 66 | ctx.putImageData(imgData, 0, 0); 67 | }-*/; 68 | 69 | } 70 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/emu/com/badlogic/gdx/graphics/g2d/freetype/FreetypeFontLoader.java: -------------------------------------------------------------------------------- 1 | package com.badlogic.gdx.graphics.g2d.freetype; 2 | 3 | import com.badlogic.gdx.assets.AssetDescriptor; 4 | import com.badlogic.gdx.assets.AssetLoaderParameters; 5 | import com.badlogic.gdx.assets.AssetManager; 6 | import com.badlogic.gdx.assets.loaders.AsynchronousAssetLoader; 7 | import com.badlogic.gdx.assets.loaders.FileHandleResolver; 8 | import com.badlogic.gdx.files.FileHandle; 9 | import com.badlogic.gdx.graphics.g2d.BitmapFont; 10 | import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData; 11 | import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeBitmapFontData; 12 | import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; 13 | import com.badlogic.gdx.utils.Array; 14 | 15 | /** 16 | * Creates {@link BitmapFont} instances from FreeType font files. Requires a {@link FreeTypeFontLoaderParameter} to be 17 | * passed to {@link AssetManager#load(String, Class, AssetLoaderParameters)} which specifies the name of the TTF 18 | * file as well the parameters used to generate the BitmapFont (size, characters, etc.) 19 | */ 20 | public class FreetypeFontLoader extends AsynchronousAssetLoader{ 21 | public FreetypeFontLoader (FileHandleResolver resolver) { 22 | super(resolver); 23 | } 24 | 25 | public static class FreeTypeFontLoaderParameter extends AssetLoaderParameters{ 26 | /** the name of the TTF file to be used to load the font **/ 27 | public String fontFileName; 28 | /** the parameters used to generate the font, e.g. size, characters, etc. **/ 29 | public FreeTypeFontParameter fontParameters = new FreeTypeFontParameter(); 30 | } 31 | 32 | @Override 33 | public void loadAsync (AssetManager manager, String fileName, FileHandle file, FreeTypeFontLoaderParameter parameter) { 34 | if(parameter == null) throw new RuntimeException("FreetypeFontParameter must be set in AssetManager#load to point at a TTF file!"); 35 | } 36 | 37 | @Override 38 | public BitmapFont loadSync (AssetManager manager, String fileName, FileHandle file, FreeTypeFontLoaderParameter parameter) { 39 | if(parameter == null) throw new RuntimeException("FreetypeFontParameter must be set in AssetManager#load to point at a TTF file!"); 40 | FreeTypeFontGenerator generator = manager.get(parameter.fontFileName + ".gen", FreeTypeFontGenerator.class); 41 | BitmapFont font = generator.generateFont(parameter.fontParameters); 42 | return font; 43 | } 44 | 45 | @Override 46 | public Array getDependencies (String fileName, FileHandle file, FreeTypeFontLoaderParameter parameter) { 47 | Array deps = new Array(); 48 | deps.add(new AssetDescriptor(parameter.fontFileName + ".gen", FreeTypeFontGenerator.class)); 49 | return deps; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/emu/com/badlogic/gdx/graphics/g2d/freetype/FreeTypeFontGenerator.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package com.badlogic.gdx.graphics.g2d.freetype; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | import com.badlogic.gdx.Gdx; 22 | import com.badlogic.gdx.files.FileHandle; 23 | import com.badlogic.gdx.graphics.Color; 24 | import com.badlogic.gdx.graphics.FreeTypePixmap; 25 | import com.badlogic.gdx.graphics.Pixmap; 26 | import com.badlogic.gdx.graphics.Pixmap.Blending; 27 | import com.badlogic.gdx.graphics.Pixmap.Format; 28 | import com.badlogic.gdx.graphics.Texture.TextureFilter; 29 | import com.badlogic.gdx.graphics.g2d.BitmapFont; 30 | import com.badlogic.gdx.graphics.g2d.BitmapFont.BitmapFontData; 31 | import com.badlogic.gdx.graphics.g2d.BitmapFont.Glyph; 32 | import com.badlogic.gdx.graphics.g2d.GlyphLayout.GlyphRun; 33 | import com.badlogic.gdx.graphics.g2d.PixmapPacker; 34 | import com.badlogic.gdx.graphics.g2d.PixmapPacker.GuillotineStrategy; 35 | import com.badlogic.gdx.graphics.g2d.PixmapPacker.PackStrategy; 36 | import com.badlogic.gdx.graphics.g2d.PixmapPacker.SkylineStrategy; 37 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 38 | import com.badlogic.gdx.graphics.g2d.freetype.FreeType.Bitmap; 39 | import com.badlogic.gdx.graphics.g2d.freetype.FreeType.Face; 40 | import com.badlogic.gdx.graphics.g2d.freetype.FreeType.GlyphMetrics; 41 | import com.badlogic.gdx.graphics.g2d.freetype.FreeType.GlyphSlot; 42 | import com.badlogic.gdx.graphics.g2d.freetype.FreeType.Library; 43 | import com.badlogic.gdx.graphics.g2d.freetype.FreeType.SizeMetrics; 44 | import com.badlogic.gdx.graphics.g2d.freetype.FreeType.Stroker; 45 | import com.badlogic.gdx.math.MathUtils; 46 | import com.badlogic.gdx.math.Rectangle; 47 | import com.badlogic.gdx.utils.Array; 48 | import com.badlogic.gdx.utils.Disposable; 49 | import com.badlogic.gdx.utils.GdxRuntimeException; 50 | 51 | /** Generates {@link BitmapFont} and {@link BitmapFontData} instances from TrueType, OTF, and other FreeType supported fonts. 52 | *

53 | * 54 | * Usage example: 55 | * 56 | *
 57 |  * FreeTypeFontGenerator gen = new FreeTypeFontGenerator(Gdx.files.internal("myfont.ttf"));
 58 |  * BitmapFont font = gen.generateFont(16);
 59 |  * gen.dispose(); // Don't dispose if doing incremental glyph generation.
 60 |  * 
61 | * 62 | * The generator has to be disposed once it is no longer used. The returned {@link BitmapFont} instances are managed by the user 63 | * and have to be disposed as usual. 64 | * 65 | * @author mzechner 66 | * @author Nathan Sweet 67 | * @author Rob Rendell */ 68 | public class FreeTypeFontGenerator implements Disposable { 69 | static public final String DEFAULT_CHARS = "\u0000ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\"!`?'.,;:()[]{}<>|/@\\^$€-%+=#_&~*\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF"; 70 | 71 | /** A hint to scale the texture as needed, without capping it at any maximum size */ 72 | static public final int NO_MAXIMUM = -1; 73 | 74 | /** The maximum texture size allowed by generateData, when storing in a texture atlas. Multiple texture pages will be created 75 | * if necessary. Default is 1024. 76 | * @see #setMaxTextureSize(int) */ 77 | static private int maxTextureSize = 1024; 78 | 79 | final Library library; 80 | final Face face; 81 | final String name; 82 | boolean bitmapped = false; 83 | private int pixelWidth, pixelHeight; 84 | 85 | /** {@link #FreeTypeFontGenerator(FileHandle, int)} */ 86 | public FreeTypeFontGenerator (FileHandle fontFile) { 87 | this(fontFile, 0); 88 | } 89 | 90 | /** Creates a new generator from the given font file. Uses {@link FileHandle#length()} to determine the file size. If the file 91 | * length could not be determined (it was 0), an extra copy of the font bytes is performed. Throws a 92 | * {@link GdxRuntimeException} if loading did not succeed. */ 93 | public FreeTypeFontGenerator (FileHandle fontFile, int faceIndex) { 94 | name = fontFile.nameWithoutExtension(); 95 | library = FreeType.initFreeType(); 96 | face = library.newFace(fontFile, faceIndex); 97 | if (checkForBitmapFont()) return; 98 | setPixelSizes(0, 15); 99 | } 100 | 101 | private int getLoadingFlags (FreeTypeFontParameter parameter) { 102 | int loadingFlags = FreeType.FT_LOAD_DEFAULT; 103 | switch (parameter.hinting) { 104 | case None: 105 | loadingFlags |= FreeType.FT_LOAD_NO_HINTING; 106 | break; 107 | case Slight: 108 | loadingFlags |= FreeType.FT_LOAD_TARGET_LIGHT; 109 | break; 110 | case Medium: 111 | loadingFlags |= FreeType.FT_LOAD_TARGET_NORMAL; 112 | break; 113 | case Full: 114 | loadingFlags |= FreeType.FT_LOAD_TARGET_MONO; 115 | break; 116 | case AutoSlight: 117 | loadingFlags |= FreeType.FT_LOAD_FORCE_AUTOHINT | FreeType.FT_LOAD_TARGET_LIGHT; 118 | break; 119 | case AutoMedium: 120 | loadingFlags |= FreeType.FT_LOAD_FORCE_AUTOHINT | FreeType.FT_LOAD_TARGET_NORMAL; 121 | break; 122 | case AutoFull: 123 | loadingFlags |= FreeType.FT_LOAD_FORCE_AUTOHINT | FreeType.FT_LOAD_TARGET_MONO; 124 | break; 125 | } 126 | return loadingFlags; 127 | } 128 | 129 | private boolean loadChar (int c) { 130 | return loadChar(c, FreeType.FT_LOAD_DEFAULT | FreeType.FT_LOAD_FORCE_AUTOHINT); 131 | } 132 | 133 | private boolean loadChar (int c, int flags) { 134 | return face.loadChar(c, flags); 135 | } 136 | 137 | private boolean checkForBitmapFont () { 138 | int faceFlags = face.getFaceFlags(); 139 | if (((faceFlags & FreeType.FT_FACE_FLAG_FIXED_SIZES) == FreeType.FT_FACE_FLAG_FIXED_SIZES) 140 | && ((faceFlags & FreeType.FT_FACE_FLAG_HORIZONTAL) == FreeType.FT_FACE_FLAG_HORIZONTAL)) { 141 | if (loadChar(32)) { 142 | GlyphSlot slot = face.getGlyph(); 143 | if (slot.getFormat() == 1651078259) { 144 | bitmapped = true; 145 | } 146 | } 147 | } 148 | return bitmapped; 149 | } 150 | 151 | public BitmapFont generateFont (FreeTypeFontParameter parameter) { 152 | return generateFont(parameter, new FreeTypeBitmapFontData()); 153 | } 154 | 155 | /** Generates a new {@link BitmapFont}. The size is expressed in pixels. Throws a GdxRuntimeException if the font could not be 156 | * generated. Using big sizes might cause such an exception. 157 | * @param parameter configures how the font is generated */ 158 | public BitmapFont generateFont (FreeTypeFontParameter parameter, FreeTypeBitmapFontData data) { 159 | boolean updateTextureRegions = data.regions == null && parameter.packer != null; 160 | if (updateTextureRegions) data.regions = new Array(); 161 | generateData(parameter, data); 162 | if (updateTextureRegions) 163 | parameter.packer.updateTextureRegions(data.regions, parameter.minFilter, parameter.magFilter, parameter.genMipMaps); 164 | if (data.regions.isEmpty()) throw new GdxRuntimeException("Unable to create a font with no texture regions."); 165 | BitmapFont font = new BitmapFont(data, data.regions, true); 166 | font.setOwnsTexture(parameter.packer == null); 167 | return font; 168 | } 169 | 170 | /** Uses ascender and descender of font to calculate real height that makes all glyphs to fit in given pixel size. Source: 171 | * http://nothings.org/stb/stb_truetype.h / stbtt_ScaleForPixelHeight */ 172 | public int scaleForPixelHeight (int height) { 173 | setPixelSizes(0, height); 174 | SizeMetrics fontMetrics = face.getSize().getMetrics(); 175 | int ascent = FreeType.toInt(fontMetrics.getAscender()); 176 | int descent = FreeType.toInt(fontMetrics.getDescender()); 177 | return height * height / (ascent - descent); 178 | } 179 | 180 | /** Uses max advance, ascender and descender of font to calculate real height that makes any n glyphs to fit in given pixel 181 | * width. 182 | * @param width the max width to fit (in pixels) 183 | * @param numChars max number of characters that to fill width */ 184 | public int scaleForPixelWidth (int width, int numChars) { 185 | SizeMetrics fontMetrics = face.getSize().getMetrics(); 186 | int advance = FreeType.toInt(fontMetrics.getMaxAdvance()); 187 | int ascent = FreeType.toInt(fontMetrics.getAscender()); 188 | int descent = FreeType.toInt(fontMetrics.getDescender()); 189 | int unscaledHeight = ascent - descent; 190 | int height = unscaledHeight * width / (advance * numChars); 191 | setPixelSizes(0, height); 192 | return height; 193 | } 194 | 195 | /** Uses max advance, ascender and descender of font to calculate real height that makes any n glyphs to fit in given pixel 196 | * width and height. 197 | * @param width the max width to fit (in pixels) 198 | * @param height the max height to fit (in pixels) 199 | * @param numChars max number of characters that to fill width */ 200 | public int scaleToFitSquare (int width, int height, int numChars) { 201 | return Math.min(scaleForPixelHeight(height), scaleForPixelWidth(width, numChars)); 202 | } 203 | 204 | public class GlyphAndBitmap { 205 | public Glyph glyph; 206 | public Bitmap bitmap; 207 | } 208 | 209 | /** Returns null if glyph was not found. If there is nothing to render, for example with various space characters, then bitmap 210 | * is null. */ 211 | public GlyphAndBitmap generateGlyphAndBitmap (int c, int size, boolean flip) { 212 | setPixelSizes(0, size); 213 | 214 | SizeMetrics fontMetrics = face.getSize().getMetrics(); 215 | int baseline = FreeType.toInt(fontMetrics.getAscender()); 216 | 217 | // Check if character exists in this font. 218 | // 0 means 'undefined character code' 219 | if (face.getCharIndex(c) == 0) { 220 | return null; 221 | } 222 | 223 | // Try to load character 224 | if (!loadChar(c)) { 225 | throw new GdxRuntimeException("Unable to load character!"); 226 | } 227 | 228 | GlyphSlot slot = face.getGlyph(); 229 | 230 | // Try to render to bitmap 231 | Bitmap bitmap; 232 | if (bitmapped) { 233 | bitmap = slot.getBitmap(); 234 | } else if (!slot.renderGlyph(FreeType.FT_RENDER_MODE_NORMAL)) { 235 | bitmap = null; 236 | } else { 237 | bitmap = slot.getBitmap(); 238 | } 239 | 240 | GlyphMetrics metrics = slot.getMetrics(); 241 | 242 | Glyph glyph = new Glyph(); 243 | if (bitmap != null) { 244 | glyph.width = bitmap.getWidth(); 245 | glyph.height = bitmap.getRows(); 246 | } else { 247 | glyph.width = 0; 248 | glyph.height = 0; 249 | } 250 | glyph.xoffset = slot.getBitmapLeft(); 251 | glyph.yoffset = flip ? -slot.getBitmapTop() + baseline : -(glyph.height - slot.getBitmapTop()) - baseline; 252 | glyph.xadvance = FreeType.toInt(metrics.getHoriAdvance()); 253 | glyph.srcX = 0; 254 | glyph.srcY = 0; 255 | glyph.id = c; 256 | 257 | GlyphAndBitmap result = new GlyphAndBitmap(); 258 | result.glyph = glyph; 259 | result.bitmap = bitmap; 260 | return result; 261 | } 262 | 263 | /** Generates a new {@link BitmapFontData} instance, expert usage only. Throws a GdxRuntimeException if something went wrong. 264 | * @param size the size in pixels */ 265 | public FreeTypeBitmapFontData generateData (int size) { 266 | FreeTypeFontParameter parameter = new FreeTypeFontParameter(); 267 | parameter.size = size; 268 | return generateData(parameter); 269 | } 270 | 271 | public FreeTypeBitmapFontData generateData (FreeTypeFontParameter parameter) { 272 | return generateData(parameter, new FreeTypeBitmapFontData()); 273 | } 274 | 275 | void setPixelSizes (int pixelWidth, int pixelHeight) { 276 | this.pixelWidth = pixelWidth; 277 | this.pixelHeight = pixelHeight; 278 | if (!bitmapped && !face.setPixelSizes(pixelWidth, pixelHeight)) throw new GdxRuntimeException("Couldn't set size for font"); 279 | } 280 | 281 | /** Generates a new {@link BitmapFontData} instance, expert usage only. Throws a GdxRuntimeException if something went wrong. 282 | * @param parameter configures how the font is generated */ 283 | public FreeTypeBitmapFontData generateData (FreeTypeFontParameter parameter, FreeTypeBitmapFontData data) { 284 | data.name = name + "-" + parameter.size; 285 | parameter = parameter == null ? new FreeTypeFontParameter() : parameter; 286 | char[] characters = parameter.characters.toCharArray(); 287 | int charactersLength = characters.length; 288 | boolean incremental = parameter.incremental; 289 | int flags = getLoadingFlags(parameter); 290 | 291 | setPixelSizes(0, parameter.size); 292 | 293 | // set general font data 294 | SizeMetrics fontMetrics = face.getSize().getMetrics(); 295 | data.flipped = parameter.flip; 296 | data.ascent = FreeType.toInt(fontMetrics.getAscender()); 297 | data.descent = FreeType.toInt(fontMetrics.getDescender()); 298 | data.lineHeight = FreeType.toInt(fontMetrics.getHeight()); 299 | float baseLine = data.ascent; 300 | 301 | // if bitmapped 302 | if (bitmapped && (data.lineHeight == 0)) { 303 | for (int c = 32; c < (32 + face.getNumGlyphs()); c++) { 304 | if (loadChar(c, flags)) { 305 | int lh = FreeType.toInt(face.getGlyph().getMetrics().getHeight()); 306 | data.lineHeight = (lh > data.lineHeight) ? lh : data.lineHeight; 307 | } 308 | } 309 | } 310 | data.lineHeight += parameter.spaceY; 311 | 312 | // determine space width 313 | if (loadChar(' ', flags) || loadChar('l', flags)) { 314 | data.spaceXadvance = FreeType.toInt(face.getGlyph().getMetrics().getHoriAdvance()); 315 | } else { 316 | data.spaceXadvance = face.getMaxAdvanceWidth(); // Possibly very wrong. 317 | } 318 | 319 | // determine x-height 320 | for (char xChar : data.xChars) { 321 | if (!loadChar(xChar, flags)) continue; 322 | data.xHeight = FreeType.toInt(face.getGlyph().getMetrics().getHeight()); 323 | break; 324 | } 325 | if (data.xHeight == 0) throw new GdxRuntimeException("No x-height character found in font"); 326 | 327 | // determine cap height 328 | for (char capChar : data.capChars) { 329 | if (!loadChar(capChar, flags)) continue; 330 | data.capHeight = FreeType.toInt(face.getGlyph().getMetrics().getHeight()) + Math.abs(parameter.shadowOffsetY); 331 | break; 332 | } 333 | if (!bitmapped && data.capHeight == 1) throw new GdxRuntimeException("No cap character found in font"); 334 | 335 | data.ascent -= data.capHeight; 336 | data.down = -data.lineHeight; 337 | if (parameter.flip) { 338 | data.ascent = -data.ascent; 339 | data.down = -data.down; 340 | } 341 | 342 | boolean ownsAtlas = false; 343 | 344 | PixmapPacker packer = parameter.packer; 345 | 346 | if (packer == null) { 347 | // Create a packer. 348 | int size; 349 | PackStrategy packStrategy; 350 | if (incremental) { 351 | size = maxTextureSize; 352 | packStrategy = new GuillotineStrategy(); 353 | } else { 354 | int maxGlyphHeight = (int)Math.ceil(data.lineHeight); 355 | size = MathUtils.nextPowerOfTwo((int)Math.sqrt(maxGlyphHeight * maxGlyphHeight * charactersLength)); 356 | if (maxTextureSize > 0) size = Math.min(size, maxTextureSize); 357 | packStrategy = new SkylineStrategy(); 358 | } 359 | ownsAtlas = true; 360 | packer = new PixmapPacker(size, size, Format.RGBA8888, 1, false, packStrategy); 361 | packer.setTransparentColor(parameter.color); 362 | packer.getTransparentColor().a = 0; 363 | if (parameter.borderWidth > 0) { 364 | packer.setTransparentColor(parameter.borderColor); 365 | packer.getTransparentColor().a = 0; 366 | } 367 | } 368 | 369 | if (incremental) data.glyphs = new Array(charactersLength + 32); 370 | 371 | Stroker stroker = null; 372 | if (parameter.borderWidth > 0) { 373 | stroker = library.createStroker(); 374 | stroker.set((int)(parameter.borderWidth * 64f), 375 | parameter.borderStraight ? FreeType.FT_STROKER_LINECAP_BUTT : FreeType.FT_STROKER_LINECAP_ROUND, 376 | parameter.borderStraight ? FreeType.FT_STROKER_LINEJOIN_MITER_FIXED : FreeType.FT_STROKER_LINEJOIN_ROUND, 0); 377 | } 378 | 379 | // Create glyphs largest height first for best packing. 380 | int[] heights = new int[charactersLength]; 381 | for (int i = 0; i < charactersLength; i++) { 382 | char c = characters[i]; 383 | 384 | int height = loadChar(c, flags) ? FreeType.toInt(face.getGlyph().getMetrics().getHeight()) : 0; 385 | heights[i] = height; 386 | 387 | if (c == '\0') { 388 | Glyph missingGlyph = createGlyph('\0', data, parameter, stroker, baseLine, packer); 389 | if (missingGlyph != null && missingGlyph.width != 0 && missingGlyph.height != 0) { 390 | data.setGlyph('\0', missingGlyph); 391 | data.missingGlyph = missingGlyph; 392 | if (incremental) data.glyphs.add(missingGlyph); 393 | } 394 | } 395 | } 396 | int heightsCount = heights.length; 397 | while (heightsCount > 0) { 398 | int best = 0, maxHeight = heights[0]; 399 | for (int i = 1; i < heightsCount; i++) { 400 | int height = heights[i]; 401 | if (height > maxHeight) { 402 | maxHeight = height; 403 | best = i; 404 | } 405 | } 406 | 407 | char c = characters[best]; 408 | if (data.getGlyph(c) == null) { 409 | Glyph glyph = createGlyph(c, data, parameter, stroker, baseLine, packer); 410 | if (glyph != null) { 411 | data.setGlyph(c, glyph); 412 | if (incremental) data.glyphs.add(glyph); 413 | } 414 | } 415 | 416 | heightsCount--; 417 | heights[best] = heights[heightsCount]; 418 | char tmpChar = characters[best]; 419 | characters[best] = characters[heightsCount]; 420 | characters[heightsCount] = tmpChar; 421 | } 422 | 423 | if (stroker != null && !incremental) stroker.dispose(); 424 | 425 | if (incremental) { 426 | data.generator = this; 427 | data.parameter = parameter; 428 | data.stroker = stroker; 429 | data.packer = packer; 430 | } 431 | 432 | // Generate kerning. 433 | parameter.kerning &= face.hasKerning(); 434 | if (parameter.kerning) { 435 | for (int i = 0; i < charactersLength; i++) { 436 | char firstChar = characters[i]; 437 | Glyph first = data.getGlyph(firstChar); 438 | if (first == null) continue; 439 | int firstIndex = face.getCharIndex(firstChar); 440 | for (int ii = i; ii < charactersLength; ii++) { 441 | char secondChar = characters[ii]; 442 | Glyph second = data.getGlyph(secondChar); 443 | if (second == null) continue; 444 | int secondIndex = face.getCharIndex(secondChar); 445 | 446 | int kerning = face.getKerning(firstIndex, secondIndex, 0); // FT_KERNING_DEFAULT (scaled then rounded). 447 | if (kerning != 0) first.setKerning(secondChar, FreeType.toInt(kerning)); 448 | 449 | kerning = face.getKerning(secondIndex, firstIndex, 0); // FT_KERNING_DEFAULT (scaled then rounded). 450 | if (kerning != 0) second.setKerning(firstChar, FreeType.toInt(kerning)); 451 | } 452 | } 453 | } 454 | 455 | // Generate texture regions. 456 | if (ownsAtlas) { 457 | data.regions = new Array(); 458 | packer.updateTextureRegions(data.regions, parameter.minFilter, parameter.magFilter, parameter.genMipMaps); 459 | } 460 | 461 | // Set space glyph. 462 | Glyph spaceGlyph = data.getGlyph(' '); 463 | if (spaceGlyph == null) { 464 | spaceGlyph = new Glyph(); 465 | spaceGlyph.xadvance = (int)data.spaceXadvance + parameter.spaceX; 466 | spaceGlyph.id = (int)' '; 467 | data.setGlyph(' ', spaceGlyph); 468 | } 469 | if (spaceGlyph.width == 0) spaceGlyph.width = (int)(spaceGlyph.xadvance + data.padRight); 470 | 471 | return data; 472 | } 473 | 474 | /** @return null if glyph was not found. */ 475 | Glyph createGlyph (char c, FreeTypeBitmapFontData data, FreeTypeFontParameter parameter, Stroker stroker, float baseLine, 476 | PixmapPacker packer) { 477 | 478 | boolean missing = face.getCharIndex(c) == 0 && c != 0; 479 | if (missing) return null; 480 | 481 | if (!loadChar(c, getLoadingFlags(parameter))) return null; 482 | 483 | GlyphSlot slot = face.getGlyph(); 484 | FreeType.Glyph mainGlyph = slot.getGlyph(); 485 | try { 486 | mainGlyph.toBitmap(parameter.mono ? FreeType.FT_RENDER_MODE_MONO : FreeType.FT_RENDER_MODE_NORMAL); 487 | } catch (GdxRuntimeException e) { 488 | mainGlyph.dispose(); 489 | Gdx.app.log("FreeTypeFontGenerator", "Couldn't render char: " + c); 490 | return null; 491 | } 492 | Bitmap mainBitmap = mainGlyph.getBitmap(); 493 | Pixmap mainPixmap = mainBitmap.getPixmap(Format.RGBA8888, parameter.color, parameter.gamma); 494 | 495 | if (mainBitmap.getWidth() != 0 && mainBitmap.getRows() != 0) { 496 | int offsetX = 0, offsetY = 0; 497 | if (parameter.borderWidth > 0) { 498 | // execute stroker; this generates a glyph "extended" along the outline 499 | int top = mainGlyph.getTop(), left = mainGlyph.getLeft(); 500 | FreeType.Glyph borderGlyph = slot.getGlyph(); 501 | borderGlyph.strokeBorder(stroker, false); 502 | borderGlyph.toBitmap(parameter.mono ? FreeType.FT_RENDER_MODE_MONO : FreeType.FT_RENDER_MODE_NORMAL); 503 | offsetX = left - borderGlyph.getLeft(); 504 | offsetY = -(top - borderGlyph.getTop()); 505 | 506 | // Render border (pixmap is bigger than main). 507 | Bitmap borderBitmap = borderGlyph.getBitmap(); 508 | Pixmap borderPixmap = borderBitmap.getPixmap(Format.RGBA8888, parameter.borderColor, parameter.borderGamma); 509 | 510 | // Draw main glyph on top of border. 511 | for (int i = 0, n = parameter.renderCount; i < n; i++) 512 | borderPixmap.drawPixmap(mainPixmap, offsetX, offsetY); 513 | 514 | mainPixmap.dispose(); 515 | mainGlyph.dispose(); 516 | mainPixmap = borderPixmap; 517 | mainGlyph = borderGlyph; 518 | } 519 | 520 | if (parameter.shadowOffsetX != 0 || parameter.shadowOffsetY != 0) { 521 | int mainW = mainPixmap.getWidth(), mainH = mainPixmap.getHeight(); 522 | int shadowOffsetX = Math.max(parameter.shadowOffsetX, 0), shadowOffsetY = Math.max(parameter.shadowOffsetY, 0); 523 | int shadowW = mainW + Math.abs(parameter.shadowOffsetX), shadowH = mainH + Math.abs(parameter.shadowOffsetY); 524 | Pixmap shadowPixmap = new Pixmap(shadowW, shadowH, mainPixmap.getFormat()); 525 | 526 | Color shadowColor = parameter.shadowColor; 527 | float a = shadowColor.a; 528 | if (a != 0) { 529 | byte r = (byte)(shadowColor.r * 255), g = (byte)(shadowColor.g * 255), b = (byte)(shadowColor.b * 255); 530 | ByteBuffer mainPixels = ((FreeTypePixmap)mainPixmap).getRealPixels(); 531 | ByteBuffer shadowPixels = ((FreeTypePixmap)shadowPixmap).getRealPixels(); 532 | for (int y = 0; y < mainH; y++) { 533 | int shadowRow = shadowW * (y + shadowOffsetY) + shadowOffsetX; 534 | for (int x = 0; x < mainW; x++) { 535 | int mainPixel = (mainW * y + x) * 4; 536 | byte mainA = mainPixels.get(mainPixel + 3); 537 | if (mainA == 0) continue; 538 | int shadowPixel = (shadowRow + x) * 4; 539 | shadowPixels.put(shadowPixel, r); 540 | shadowPixels.put(shadowPixel + 1, g); 541 | shadowPixels.put(shadowPixel + 2, b); 542 | shadowPixels.put(shadowPixel + 3, (byte)((mainA & 0xff) * a)); 543 | } 544 | } 545 | ((FreeTypePixmap)shadowPixmap).putPixelsBack(shadowPixels); 546 | } 547 | 548 | // Draw main glyph (with any border) on top of shadow. 549 | for (int i = 0, n = parameter.renderCount; i < n; i++) 550 | shadowPixmap.drawPixmap(mainPixmap, Math.max(-parameter.shadowOffsetX, 0), Math.max(-parameter.shadowOffsetY, 0)); 551 | mainPixmap.dispose(); 552 | mainPixmap = shadowPixmap; 553 | } else if (parameter.borderWidth == 0) { 554 | // No shadow and no border, draw glyph additional times. 555 | for (int i = 0, n = parameter.renderCount - 1; i < n; i++) 556 | mainPixmap.drawPixmap(mainPixmap, 0, 0); 557 | } 558 | 559 | if (parameter.padTop > 0 || parameter.padLeft > 0 || parameter.padBottom > 0 || parameter.padRight > 0) { 560 | Pixmap padPixmap = new Pixmap(mainPixmap.getWidth() + parameter.padLeft + parameter.padRight, 561 | mainPixmap.getHeight() + parameter.padTop + parameter.padBottom, mainPixmap.getFormat()); 562 | padPixmap.setBlending(Blending.None); 563 | padPixmap.drawPixmap(mainPixmap, parameter.padLeft, parameter.padTop); 564 | mainPixmap.dispose(); 565 | mainPixmap = padPixmap; 566 | } 567 | } 568 | 569 | GlyphMetrics metrics = slot.getMetrics(); 570 | Glyph glyph = new Glyph(); 571 | glyph.id = c; 572 | glyph.width = mainPixmap.getWidth(); 573 | glyph.height = mainPixmap.getHeight(); 574 | glyph.xoffset = mainGlyph.getLeft(); 575 | if (parameter.flip) 576 | glyph.yoffset = -mainGlyph.getTop() + (int)baseLine; 577 | else 578 | glyph.yoffset = -(glyph.height - mainGlyph.getTop()) - (int)baseLine; 579 | glyph.xadvance = FreeType.toInt(metrics.getHoriAdvance()) + (int)parameter.borderWidth + parameter.spaceX; 580 | 581 | if (bitmapped) { 582 | mainPixmap.setColor(Color.CLEAR); 583 | mainPixmap.fill(); 584 | ByteBuffer buf = mainBitmap.getBuffer(); 585 | int whiteIntBits = Color.WHITE.toIntBits(); 586 | int clearIntBits = Color.CLEAR.toIntBits(); 587 | for (int h = 0; h < glyph.height; h++) { 588 | int idx = h * mainBitmap.getPitch(); 589 | for (int w = 0; w < (glyph.width + glyph.xoffset); w++) { 590 | int bit = (buf.get(idx + (w / 8)) >>> (7 - (w % 8))) & 1; 591 | mainPixmap.drawPixel(w, h, ((bit == 1) ? whiteIntBits : clearIntBits)); 592 | } 593 | } 594 | } 595 | 596 | Rectangle rect = packer.pack(mainPixmap); 597 | glyph.page = packer.getPages().size - 1; // Glyph is always packed into the last page for now. 598 | glyph.srcX = (int)rect.x; 599 | glyph.srcY = (int)rect.y; 600 | 601 | // If a page was added, create a new texture region for the incrementally added glyph. 602 | if (parameter.incremental && data.regions != null && data.regions.size <= glyph.page) 603 | packer.updateTextureRegions(data.regions, parameter.minFilter, parameter.magFilter, parameter.genMipMaps); 604 | 605 | mainPixmap.dispose(); 606 | mainGlyph.dispose(); 607 | 608 | return glyph; 609 | } 610 | 611 | public String toString () { 612 | return name; 613 | } 614 | 615 | /** Cleans up all resources of the generator. Call this if you no longer use the generator. */ 616 | @Override 617 | public void dispose () { 618 | face.dispose(); 619 | library.dispose(); 620 | } 621 | 622 | /** Sets the maximum size that will be used when generating texture atlases for glyphs with generateData(). The 623 | * default is 1024. By specifying {@link #NO_MAXIMUM}, the texture atlas will scale as needed. 624 | * 625 | * The power-of-two square texture size will be capped to the given texSize. It's recommended that a power-of-two 626 | * value be used here. 627 | * 628 | * Multiple pages may be used to fit all the generated glyphs. You can query the resulting number of pages by calling 629 | * bitmapFont.getRegions().length or freeTypeBitmapFontData.getTextureRegions().length. 630 | * 631 | * If PixmapPacker is specified when calling generateData, this parameter is ignored. 632 | * 633 | * @param texSize the maximum texture size for one page of glyphs */ 634 | public static void setMaxTextureSize (int texSize) { 635 | maxTextureSize = texSize; 636 | } 637 | 638 | /** Returns the maximum texture size that will be used by generateData() when creating a texture atlas for the glyphs. 639 | * @return the power-of-two max texture size */ 640 | public static int getMaxTextureSize () { 641 | return maxTextureSize; 642 | } 643 | 644 | /** {@link BitmapFontData} used for fonts generated via the {@link FreeTypeFontGenerator}. The texture storing the glyphs is 645 | * held in memory, thus the {@link #getImagePaths()} and {@link #getFontFile()} methods will return null. 646 | * @author mzechner 647 | * @author Nathan Sweet */ 648 | static public class FreeTypeBitmapFontData extends BitmapFontData implements Disposable { 649 | public Array regions; 650 | 651 | // Fields for incremental glyph generation. 652 | FreeTypeFontGenerator generator; 653 | FreeTypeFontParameter parameter; 654 | Stroker stroker; 655 | PixmapPacker packer; 656 | Array glyphs; 657 | private boolean dirty; 658 | 659 | @Override 660 | public Glyph getGlyph (char ch) { 661 | Glyph glyph = super.getGlyph(ch); 662 | if (glyph == null && generator != null) { 663 | generator.setPixelSizes(0, parameter.size); 664 | float baseline = ((flipped ? -ascent : ascent) + capHeight) / scaleY; 665 | glyph = generator.createGlyph(ch, this, parameter, stroker, baseline, packer); 666 | if (glyph == null) return missingGlyph; 667 | 668 | setGlyphRegion(glyph, regions.get(glyph.page)); 669 | setGlyph(ch, glyph); 670 | glyphs.add(glyph); 671 | dirty = true; 672 | 673 | Face face = generator.face; 674 | if (parameter.kerning) { 675 | int glyphIndex = face.getCharIndex(ch); 676 | for (int i = 0, n = glyphs.size; i < n; i++) { 677 | Glyph other = glyphs.get(i); 678 | int otherIndex = face.getCharIndex(other.id); 679 | 680 | int kerning = face.getKerning(glyphIndex, otherIndex, 0); 681 | if (kerning != 0) glyph.setKerning(other.id, FreeType.toInt(kerning)); 682 | 683 | kerning = face.getKerning(otherIndex, glyphIndex, 0); 684 | if (kerning != 0) other.setKerning(ch, FreeType.toInt(kerning)); 685 | } 686 | } 687 | } 688 | return glyph; 689 | } 690 | 691 | public void getGlyphs (GlyphRun run, CharSequence str, int start, int end, Glyph lastGlyph) { 692 | if (packer != null) packer.setPackToTexture(true); // All glyphs added after this are packed directly to the texture. 693 | super.getGlyphs(run, str, start, end, lastGlyph); 694 | if (dirty) { 695 | dirty = false; 696 | packer.updateTextureRegions(regions, parameter.minFilter, parameter.magFilter, parameter.genMipMaps); 697 | } 698 | } 699 | 700 | @Override 701 | public void dispose () { 702 | if (stroker != null) stroker.dispose(); 703 | if (packer != null) packer.dispose(); 704 | } 705 | } 706 | 707 | /** Font smoothing algorithm. */ 708 | public static enum Hinting { 709 | /** Disable hinting. Generated glyphs will look blurry. */ 710 | None, 711 | /** Light hinting with fuzzy edges, but close to the original shape */ 712 | Slight, 713 | /** Average hinting */ 714 | Medium, 715 | /** Strong hinting with crisp edges at the expense of shape fidelity */ 716 | Full, 717 | /** Light hinting with fuzzy edges, but close to the original shape. Uses the FreeType auto-hinter. */ 718 | AutoSlight, 719 | /** Average hinting. Uses the FreeType auto-hinter. */ 720 | AutoMedium, 721 | /** Strong hinting with crisp edges at the expense of shape fidelity. Uses the FreeType auto-hinter. */ 722 | AutoFull, 723 | } 724 | 725 | /** Parameter container class that helps configure how {@link FreeTypeBitmapFontData} and {@link BitmapFont} instances are 726 | * generated. 727 | * 728 | * The packer field is for advanced usage, where it is necessary to pack multiple BitmapFonts (i.e. styles, sizes, families) 729 | * into a single Texture atlas. If no packer is specified, the generator will use its own PixmapPacker to pack the glyphs into 730 | * a power-of-two sized texture, and the resulting {@link FreeTypeBitmapFontData} will have a valid {@link TextureRegion} which 731 | * can be used to construct a new {@link BitmapFont}. 732 | * 733 | * @author siondream 734 | * @author Nathan Sweet */ 735 | public static class FreeTypeFontParameter { 736 | /** The size in pixels */ 737 | public int size = 16; 738 | /** If true, font smoothing is disabled. */ 739 | public boolean mono; 740 | /** Strength of hinting */ 741 | public Hinting hinting = Hinting.AutoMedium; 742 | /** Foreground color (required for non-black borders) */ 743 | public Color color = Color.WHITE; 744 | /** Glyph gamma. Values > 1 reduce antialiasing. */ 745 | public float gamma = 1.8f; 746 | /** Number of times to render the glyph. Useful with a shadow or border, so it doesn't show through the glyph. */ 747 | public int renderCount = 2; 748 | /** Border width in pixels, 0 to disable */ 749 | public float borderWidth = 0; 750 | /** Border color; only used if borderWidth > 0 */ 751 | public Color borderColor = Color.BLACK; 752 | /** true for straight (mitered), false for rounded borders */ 753 | public boolean borderStraight = false; 754 | /** Values < 1 increase the border size. */ 755 | public float borderGamma = 1.8f; 756 | /** Offset of text shadow on X axis in pixels, 0 to disable */ 757 | public int shadowOffsetX = 0; 758 | /** Offset of text shadow on Y axis in pixels, 0 to disable */ 759 | public int shadowOffsetY = 0; 760 | /** Shadow color; only used if shadowOffset > 0. If alpha component is 0, no shadow is drawn but characters are still offset 761 | * by shadowOffset. */ 762 | public Color shadowColor = new Color(0, 0, 0, 0.75f); 763 | /** Pixels to add to glyph spacing when text is rendered. Can be negative. */ 764 | public int spaceX, spaceY; 765 | /** Pixels to add to the glyph in the texture. Cannot be negative. */ 766 | public int padTop, padLeft, padBottom, padRight; 767 | /** The characters the font should contain. If '\0' is not included then {@link BitmapFontData#missingGlyph} is not set. */ 768 | public String characters = DEFAULT_CHARS; 769 | /** Whether the font should include kerning */ 770 | public boolean kerning = true; 771 | /** The optional PixmapPacker to use for packing multiple fonts into a single texture. 772 | * @see FreeTypeFontParameter */ 773 | public PixmapPacker packer = null; 774 | /** Whether to flip the font vertically */ 775 | public boolean flip = false; 776 | /** Whether to generate mip maps for the resulting texture */ 777 | public boolean genMipMaps = false; 778 | /** Minification filter */ 779 | public TextureFilter minFilter = TextureFilter.Nearest; 780 | /** Magnification filter */ 781 | public TextureFilter magFilter = TextureFilter.Nearest; 782 | /** When true, glyphs are rendered on the fly to the font's glyph page textures as they are needed. The 783 | * FreeTypeFontGenerator must not be disposed until the font is no longer needed. The FreeTypeBitmapFontData must be 784 | * disposed (separately from the generator) when the font is no longer needed. The FreeTypeFontParameter should not be 785 | * modified after creating a font. If a PixmapPacker is not specified, the font glyph page textures will use 786 | * {@link FreeTypeFontGenerator#getMaxTextureSize()}. */ 787 | public boolean incremental; 788 | } 789 | } 790 | -------------------------------------------------------------------------------- /src/com/badlogic/gdx/graphics/g2d/freetype/gwt/emu/com/badlogic/gdx/graphics/g2d/freetype/FreeType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2011 See AUTHORS file. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | ******************************************************************************/ 16 | 17 | package com.badlogic.gdx.graphics.g2d.freetype; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.nio.ByteBuffer; 22 | import java.nio.FreeTypeUtil; 23 | import java.nio.IntBuffer; 24 | 25 | import java.nio.HasArrayBufferView; 26 | import com.badlogic.gdx.files.FileHandle; 27 | import com.badlogic.gdx.graphics.Color; 28 | import com.badlogic.gdx.graphics.FreeTypePixmap; 29 | import com.badlogic.gdx.graphics.Pixmap; 30 | import com.badlogic.gdx.graphics.Pixmap.Blending; 31 | import com.badlogic.gdx.graphics.Pixmap.Format; 32 | import com.badlogic.gdx.utils.BufferUtils; 33 | import com.badlogic.gdx.utils.Disposable; 34 | import com.badlogic.gdx.utils.GdxRuntimeException; 35 | import com.badlogic.gdx.utils.LongMap; 36 | import com.badlogic.gdx.utils.StreamUtils; 37 | import com.google.gwt.typedarrays.shared.ArrayBuffer; 38 | import com.google.gwt.typedarrays.shared.ArrayBufferView; 39 | import com.google.gwt.typedarrays.shared.Int8Array; 40 | 41 | public class FreeType { 42 | // @off 43 | /*JNI 44 | #include 45 | #include FT_FREETYPE_H 46 | #include FT_STROKER_H 47 | 48 | static jint lastError = 0; 49 | */ 50 | 51 | private static native void nativeFree (int address)/*-{ 52 | $wnd.Module._free(address); 53 | }-*/; 54 | 55 | /** 56 | * 57 | * @return returns the last error code FreeType reported 58 | */ 59 | static native int getLastErrorCode()/*-{ 60 | return $wnd.Module._c_FreeType_getLastErrorCode(); 61 | }-*/; 62 | 63 | private static class Pointer { 64 | int address; 65 | 66 | Pointer(int address) { 67 | this.address = address; 68 | } 69 | } 70 | 71 | public static class Library extends Pointer implements Disposable { 72 | LongMap fontData = new LongMap(); 73 | 74 | Library (int address) { 75 | super(address); 76 | } 77 | 78 | @Override 79 | public void dispose () { 80 | doneFreeType(address); 81 | for(Integer address: fontData.values()) { 82 | nativeFree(address); 83 | } 84 | } 85 | 86 | private static native void doneFreeType (int library)/*-{ 87 | $wnd.Module._c_Library_doneFreeType(library); 88 | }-*/; 89 | 90 | public Face newFace(FileHandle fontFile, int faceIndex) { 91 | ByteBuffer buffer = null; 92 | try { 93 | //buffer = fontFile.map(); //method missing in gwt emulation 94 | } catch (GdxRuntimeException ignored) { 95 | // OK to ignore, some platforms do not support file mapping. 96 | } 97 | if (buffer == null) { 98 | InputStream input = fontFile.read(); 99 | try { 100 | int fileSize = (int)fontFile.length(); 101 | if (fileSize == 0) { 102 | // Copy to a byte[] to get the size, then copy to the buffer. 103 | byte[] data = StreamUtils.copyStreamToByteArray(input, 1024 * 16); 104 | buffer = BufferUtils.newByteBuffer(data.length); 105 | BufferUtils.copy(data, 0, buffer, data.length); 106 | } else { 107 | // Trust the specified file size. 108 | buffer = BufferUtils.newByteBuffer(fileSize); 109 | StreamUtils.copyStream(input, buffer); 110 | } 111 | } catch (IOException ex) { 112 | throw new GdxRuntimeException(ex); 113 | } finally { 114 | StreamUtils.closeQuietly(input); 115 | } 116 | } 117 | return newMemoryFace(buffer, faceIndex); 118 | } 119 | 120 | public Face newMemoryFace(byte[] data, int dataSize, int faceIndex) { 121 | ByteBuffer buffer = BufferUtils.newByteBuffer(data.length); 122 | BufferUtils.copy(data, 0, buffer, data.length); 123 | return newMemoryFace(buffer, faceIndex); 124 | } 125 | 126 | public Face newMemoryFace(ByteBuffer buffer, int faceIndex) { 127 | ArrayBufferView buf = ((HasArrayBufferView)buffer).getTypedArray(); 128 | int[] addressToFree = new int[] {0}; // Hacky way to get two return values 129 | int face = newMemoryFace(address, buf, buffer.remaining(), faceIndex, addressToFree); 130 | if (face == 0) { 131 | if (addressToFree[0] != 0) { // 'Zero' would mean allocating the buffer failed 132 | nativeFree(addressToFree[0]); 133 | } 134 | throw new GdxRuntimeException("Couldn't load font, FreeType error code: " + getLastErrorCode()); 135 | } 136 | else { 137 | fontData.put(face, addressToFree[0]); 138 | return new Face(face, this); 139 | } 140 | } 141 | 142 | private static native int newMemoryFace (int library, ArrayBufferView data, int dataSize, int faceIndex, 143 | int[] outAddressToFree)/*-{ 144 | var address = $wnd.Module._malloc(data.length); 145 | outAddressToFree[0] = address; 146 | $wnd.Module.writeArrayToMemory(data, address); 147 | return $wnd.Module._c_Library_newMemoryFace(library, address, 148 | dataSize, faceIndex); 149 | }-*/; 150 | 151 | public Stroker createStroker() { 152 | int stroker = strokerNew(address); 153 | if(stroker == 0) throw new GdxRuntimeException("Couldn't create FreeType stroker, FreeType error code: " + getLastErrorCode()); 154 | return new Stroker(stroker); 155 | } 156 | 157 | private static native int strokerNew (int library)/*-{ 158 | return $wnd.Module._c_Library_strokerNew(library); 159 | }-*/; 160 | } 161 | 162 | public static class Face extends Pointer implements Disposable { 163 | Library library; 164 | 165 | public Face (int address, Library library) { 166 | super(address); 167 | this.library = library; 168 | } 169 | 170 | @Override 171 | public void dispose () { 172 | doneFace(address); 173 | Integer freeAddress = library.fontData.get(address); 174 | if (freeAddress != 0) { // Don't free 'zero' address 175 | library.fontData.remove(address); 176 | nativeFree(freeAddress); 177 | } 178 | } 179 | 180 | private static native void doneFace (int face)/*-{ 181 | $wnd.Module._c_Face_doneFace(face); 182 | }-*/; 183 | 184 | public int getFaceFlags() { 185 | return getFaceFlags(address); 186 | } 187 | 188 | private static native int getFaceFlags (int face)/*-{ 189 | return $wnd.Module._c_Face_getFaceFlags(face); 190 | }-*/; 191 | 192 | public int getStyleFlags() { 193 | return getStyleFlags(address); 194 | } 195 | 196 | private static native int getStyleFlags (int face)/*-{ 197 | return $wnd.Module._c_Face_getStyleFlags(face); 198 | }-*/; 199 | 200 | 201 | public int getNumGlyphs() { 202 | return getNumGlyphs(address); 203 | } 204 | 205 | private static native int getNumGlyphs (int face)/*-{ 206 | return $wnd.Module._c_Face_getNumGlyphs(face); 207 | }-*/; 208 | 209 | public int getAscender() { 210 | return getAscender(address); 211 | } 212 | 213 | private static native int getAscender (int face)/*-{ 214 | return $wnd.Module._c_Face_getAscender(face); 215 | }-*/; 216 | 217 | public int getDescender() { 218 | return getDescender(address); 219 | } 220 | 221 | private static native int getDescender (int face)/*-{ 222 | return $wnd.Module._c_Face_getDescender(face); 223 | }-*/; 224 | 225 | public int getHeight() { 226 | return getHeight(address); 227 | } 228 | 229 | private static native int getHeight (int face)/*-{ 230 | return $wnd.Module._c_Face_getHeight(face); 231 | }-*/; 232 | 233 | public int getMaxAdvanceWidth() { 234 | return getMaxAdvanceWidth(address); 235 | } 236 | 237 | private static native int getMaxAdvanceWidth (int face)/*-{ 238 | return $wnd.Module._c_Face_getMaxAdvanceWidth(face); 239 | }-*/; 240 | 241 | public int getMaxAdvanceHeight() { 242 | return getMaxAdvanceHeight(address); 243 | } 244 | 245 | private static native int getMaxAdvanceHeight (int face)/*-{ 246 | return $wnd.Module._c_Face_getMaxAdvanceHeight(face); 247 | }-*/; 248 | 249 | public int getUnderlinePosition() { 250 | return getUnderlinePosition(address); 251 | } 252 | 253 | private static native int getUnderlinePosition (int face)/*-{ 254 | return $wnd.Module._c_Face_getUnderlinePosition(face); 255 | }-*/; 256 | 257 | public int getUnderlineThickness() { 258 | return getUnderlineThickness(address); 259 | } 260 | 261 | private static native int getUnderlineThickness (int face)/*-{ 262 | return $wnd.Module._c_Face_getUnderlineThickness(face); 263 | }-*/; 264 | 265 | public boolean selectSize(int strikeIndex) { 266 | return selectSize(address, strikeIndex); 267 | } 268 | 269 | private static native boolean selectSize (int face, int strike_index)/*-{ 270 | return !!$wnd.Module._c_Face_selectSize(face, strike_index); 271 | }-*/; 272 | 273 | public boolean setCharSize(int charWidth, int charHeight, int horzResolution, int vertResolution) { 274 | return setCharSize(address, charWidth, charHeight, horzResolution, vertResolution); 275 | } 276 | 277 | private static native boolean setCharSize (int face, int charWidth, int charHeight, int horzResolution, 278 | int vertResolution)/*-{ 279 | return !!$wnd.Module._c_Face_setCharSize(face, charWidth, 280 | charHeight, horzResolution, vertResolution); 281 | }-*/; 282 | 283 | public boolean setPixelSizes(int pixelWidth, int pixelHeight) { 284 | return setPixelSizes(address, pixelWidth, pixelHeight); 285 | } 286 | 287 | private static native boolean setPixelSizes (int face, int pixelWidth, int pixelHeight)/*-{ 288 | return !!$wnd.Module._c_Face_setPixelSizes(face, pixelWidth, 289 | pixelHeight); 290 | }-*/; 291 | 292 | public boolean loadGlyph(int glyphIndex, int loadFlags) { 293 | return loadGlyph(address, glyphIndex, loadFlags); 294 | } 295 | 296 | private static native boolean loadGlyph (int face, int glyphIndex, int loadFlags)/*-{ 297 | return !!$wnd.Module._c_Face_loadGlyph(face, glyphIndex, loadFlags); 298 | }-*/; 299 | 300 | public boolean loadChar(int charCode, int loadFlags) { 301 | return loadChar(address, charCode, loadFlags); 302 | } 303 | 304 | private static native boolean loadChar (int face, int charCode, int loadFlags)/*-{ 305 | return !!$wnd.Module._c_Face_loadChar(face, charCode, loadFlags); 306 | }-*/; 307 | 308 | public GlyphSlot getGlyph() { 309 | return new GlyphSlot(getGlyph(address)); 310 | } 311 | 312 | private static native int getGlyph (int face)/*-{ 313 | return $wnd.Module._c_Face_getGlyph(face); 314 | }-*/; 315 | 316 | public Size getSize() { 317 | return new Size(getSize(address)); 318 | } 319 | 320 | private static native int getSize (int face)/*-{ 321 | return $wnd.Module._c_Face_getSize(face); 322 | }-*/; 323 | 324 | public boolean hasKerning() { 325 | return hasKerning(address); 326 | } 327 | 328 | private static native boolean hasKerning (int face)/*-{ 329 | return !!$wnd.Module._c_Face_hasKerning(face); 330 | }-*/; 331 | 332 | public int getKerning(int leftGlyph, int rightGlyph, int kernMode) { 333 | return getKerning(address, leftGlyph, rightGlyph, kernMode); 334 | } 335 | 336 | private static native int getKerning (int face, int leftGlyph, int rightGlyph, int kernMode)/*-{ 337 | return $wnd.Module._c_Face_getKerning(face, leftGlyph, rightGlyph, 338 | kernMode); 339 | }-*/; 340 | 341 | public int getCharIndex(int charCode) { 342 | return getCharIndex(address, charCode); 343 | } 344 | 345 | private static native int getCharIndex (int face, int charCode)/*-{ 346 | return $wnd.Module._c_Face_getCharIndex(face, charCode); 347 | }-*/; 348 | 349 | } 350 | 351 | public static class Size extends Pointer { 352 | Size (int address) { 353 | super(address); 354 | } 355 | 356 | public SizeMetrics getMetrics() { 357 | return new SizeMetrics(getMetrics(address)); 358 | } 359 | 360 | private static native int getMetrics (int address)/*-{ 361 | return $wnd.Module._c_Size_getMetrics(address); 362 | }-*/; 363 | } 364 | 365 | public static class SizeMetrics extends Pointer { 366 | SizeMetrics (int address) { 367 | super(address); 368 | } 369 | 370 | public int getXppem() { 371 | return getXppem(address); 372 | } 373 | 374 | private static native int getXppem (int metrics)/*-{ 375 | return $wnd.Module._c_SizeMetrics_getXppem(metrics); 376 | }-*/; 377 | 378 | public int getYppem() { 379 | return getYppem(address); 380 | } 381 | 382 | private static native int getYppem (int metrics)/*-{ 383 | return $wnd.Module._c_SizeMetrics_getYppem(metrics); 384 | }-*/; 385 | 386 | public int getXScale() { 387 | return getXscale(address); 388 | } 389 | 390 | private static native int getXscale (int metrics)/*-{ 391 | return $wnd.Module._c_SizeMetrics_getXscale(metrics); 392 | }-*/; 393 | 394 | public int getYscale() { 395 | return getYscale(address); 396 | } 397 | 398 | private static native int getYscale (int metrics)/*-{ 399 | return $wnd.Module._c_SizeMetrics_getYscale(metrics); 400 | }-*/; 401 | 402 | public int getAscender() { 403 | return getAscender(address); 404 | } 405 | 406 | private static native int getAscender (int metrics)/*-{ 407 | return $wnd.Module._c_SizeMetrics_getAscender(metrics); 408 | }-*/; 409 | 410 | public int getDescender() { 411 | return getDescender(address); 412 | } 413 | 414 | private static native int getDescender (int metrics)/*-{ 415 | return $wnd.Module._c_SizeMetrics_getDescender(metrics); 416 | }-*/; 417 | 418 | public int getHeight() { 419 | return getHeight(address); 420 | } 421 | 422 | private static native int getHeight (int metrics)/*-{ 423 | return $wnd.Module._c_SizeMetrics_getHeight(metrics); 424 | }-*/; 425 | 426 | public int getMaxAdvance() { 427 | return getMaxAdvance(address); 428 | } 429 | 430 | private static native int getMaxAdvance (int metrics)/*-{ 431 | return $wnd.Module._c_SizeMetrics_getMaxAdvance(metrics); 432 | }-*/; 433 | } 434 | 435 | public static class GlyphSlot extends Pointer { 436 | GlyphSlot (int address) { 437 | super(address); 438 | } 439 | 440 | public GlyphMetrics getMetrics() { 441 | return new GlyphMetrics(getMetrics(address)); 442 | } 443 | 444 | private static native int getMetrics (int slot)/*-{ 445 | return $wnd.Module._c_GlyphSlot_getMetrics(slot); 446 | }-*/; 447 | 448 | public int getLinearHoriAdvance() { 449 | return getLinearHoriAdvance(address); 450 | } 451 | 452 | private static native int getLinearHoriAdvance (int slot)/*-{ 453 | return $wnd.Module._c_GlyphSlot_getLinearHoriAdvance(slot); 454 | }-*/; 455 | 456 | public int getLinearVertAdvance() { 457 | return getLinearVertAdvance(address); 458 | } 459 | 460 | private static native int getLinearVertAdvance (int slot)/*-{ 461 | return $wnd.Module._c_GlyphSlot_getLinearVertAdvance(slot); 462 | }-*/; 463 | 464 | public int getAdvanceX() { 465 | return getAdvanceX(address); 466 | } 467 | 468 | private static native int getAdvanceX (int slot)/*-{ 469 | return $wnd.Module._c_GlyphSlot_getAdvanceX(slot); 470 | }-*/; 471 | 472 | public int getAdvanceY() { 473 | return getAdvanceY(address); 474 | } 475 | 476 | private static native int getAdvanceY (int slot)/*-{ 477 | return $wnd.Module._c_GlyphSlot_getAdvanceY(slot); 478 | }-*/; 479 | 480 | public int getFormat() { 481 | return getFormat(address); 482 | } 483 | 484 | private static native int getFormat (int slot)/*-{ 485 | return $wnd.Module._c_GlyphSlot_getFormat(slot); 486 | }-*/; 487 | 488 | public Bitmap getBitmap() { 489 | return new Bitmap(getBitmap(address)); 490 | } 491 | 492 | private static native int getBitmap (int slot)/*-{ 493 | return $wnd.Module._c_GlyphSlot_getBitmap(slot); 494 | }-*/; 495 | 496 | public int getBitmapLeft() { 497 | return getBitmapLeft(address); 498 | } 499 | 500 | private static native int getBitmapLeft (int slot)/*-{ 501 | return $wnd.Module._c_GlyphSlot_getBitmapLeft(slot); 502 | }-*/; 503 | 504 | public int getBitmapTop() { 505 | return getBitmapTop(address); 506 | } 507 | 508 | private static native int getBitmapTop (int slot)/*-{ 509 | return $wnd.Module._c_GlyphSlot_getBitmapTop(slot); 510 | }-*/; 511 | 512 | public boolean renderGlyph(int renderMode) { 513 | return renderGlyph(address, renderMode); 514 | } 515 | 516 | private static native boolean renderGlyph (int slot, int renderMode)/*-{ 517 | return !!$wnd.Module._c_GlyphSlot_renderGlyph(slot, renderMode); 518 | }-*/; 519 | 520 | public Glyph getGlyph() { 521 | int glyph = getGlyph(address); 522 | if(glyph == 0) throw new GdxRuntimeException("Couldn't get glyph, FreeType error code: " + getLastErrorCode()); 523 | return new Glyph(glyph); 524 | } 525 | 526 | private static native int getGlyph (int glyphSlot)/*-{ 527 | return $wnd.Module._c_GlyphSlot_getGlyph(glyphSlot); 528 | }-*/; 529 | } 530 | 531 | public static class Glyph extends Pointer implements Disposable { 532 | private boolean rendered; 533 | 534 | Glyph (int address) { 535 | super(address); 536 | } 537 | 538 | @Override 539 | public void dispose () { 540 | done(address); 541 | } 542 | 543 | private static native void done (int glyph)/*-{ 544 | $wnd.Module._c_Glyph_done(glyph); 545 | }-*/; 546 | 547 | private int bTI (boolean bool) { 548 | return bool == true ? 1 : 0; 549 | } 550 | 551 | public void strokeBorder(Stroker stroker, boolean inside) { 552 | address = strokeBorder(address, stroker.address, bTI(inside)); 553 | } 554 | 555 | private static native int strokeBorder (int glyph, int stroker, int inside)/*-{ 556 | return $wnd.Module._c_Glyph_strokeBorder(glyph, stroker, inside); 557 | }-*/; 558 | 559 | public void toBitmap(int renderMode) { 560 | int bitmap = toBitmap(address, renderMode); 561 | if (bitmap == 0) throw new GdxRuntimeException("Couldn't render glyph, FreeType error code: " + getLastErrorCode()); 562 | address = bitmap; 563 | rendered = true; 564 | } 565 | 566 | private static native int toBitmap (int glyph, int renderMode)/*-{ 567 | return $wnd.Module._c_Glyph_toBitmap(glyph, renderMode); 568 | }-*/; 569 | 570 | public Bitmap getBitmap() { 571 | if (!rendered) { 572 | throw new GdxRuntimeException("Glyph is not yet rendered"); 573 | } 574 | return new Bitmap(getBitmap(address)); 575 | } 576 | 577 | private static native int getBitmap (int glyph)/*-{ 578 | return $wnd.Module._c_Glyph_getBitmap(glyph); 579 | }-*/; 580 | 581 | public int getLeft() { 582 | if (!rendered) { 583 | throw new GdxRuntimeException("Glyph is not yet rendered"); 584 | } 585 | return getLeft(address); 586 | } 587 | 588 | private static native int getLeft (int glyph)/*-{ 589 | return $wnd.Module._c_Glyph_getLeft(glyph); 590 | }-*/; 591 | 592 | public int getTop() { 593 | if (!rendered) { 594 | throw new GdxRuntimeException("Glyph is not yet rendered"); 595 | } 596 | return getTop(address); 597 | } 598 | 599 | private static native int getTop (int glyph)/*-{ 600 | return $wnd.Module._c_Glyph_getTop(glyph); 601 | }-*/; 602 | 603 | } 604 | 605 | public static class Bitmap extends Pointer { 606 | Bitmap (int address) { 607 | super(address); 608 | } 609 | 610 | public int getRows() { 611 | return getRows(address); 612 | } 613 | 614 | private static native int getRows (int bitmap)/*-{ 615 | return $wnd.Module._c_Bitmap_getRows(bitmap); 616 | }-*/; 617 | 618 | public int getWidth() { 619 | return getWidth(address); 620 | } 621 | 622 | private static native int getWidth (int bitmap)/*-{ 623 | return $wnd.Module._c_Bitmap_getWidth(bitmap); 624 | }-*/; 625 | 626 | public int getPitch() { 627 | return getPitch(address); 628 | } 629 | 630 | private static native int getPitch (int bitmap)/*-{ 631 | return $wnd.Module._c_Bitmap_getPitch(bitmap); 632 | }-*/; 633 | 634 | public ByteBuffer getBuffer () { 635 | if (getRows() == 0) 636 | // Issue #768 - CheckJNI frowns upon env->NewDirectByteBuffer with NULL buffer or capacity 0 637 | // "JNI WARNING: invalid values for address (0x0) or capacity (0)" 638 | // FreeType sets FT_Bitmap::buffer to NULL when the bitmap is empty (e.g. for ' ') 639 | // JNICheck is on by default on emulators and might have a point anyway... 640 | // So let's avoid this and just return a dummy non-null non-zero buffer 641 | return BufferUtils.newByteBuffer(1); 642 | int offset = getBufferAddress(address); 643 | int length = getBufferSize(address); 644 | Int8Array as = getBuffer(address, offset, length); 645 | ArrayBuffer aBuf = as.buffer(); 646 | ByteBuffer buf = FreeTypeUtil.newDirectReadWriteByteBuffer(aBuf, length, offset); 647 | 648 | return buf; 649 | } 650 | 651 | private static native int getBufferAddress (int bitmap)/*-{ 652 | return $wnd.Module._c_Bitmap_getBufferAddress(bitmap); 653 | }-*/; 654 | 655 | private static native int getBufferSize (int bitmap)/*-{ 656 | return $wnd.Module._c_Bitmap_getBufferSize(bitmap); 657 | }-*/; 658 | 659 | private static native Int8Array getBuffer (int bitmap, int offset, int length)/*-{ 660 | var buff = $wnd.Module.HEAP8.subarray(offset, offset + length); 661 | return buff; 662 | }-*/; 663 | 664 | // @on 665 | public Pixmap getPixmap (Format format, Color color, float gamma) { 666 | int width = getWidth(), rows = getRows(); 667 | ByteBuffer src = getBuffer(); 668 | FreeTypePixmap pixmap; 669 | ByteBuffer changedPixels; 670 | int pixelMode = getPixelMode(); 671 | int rowBytes = Math.abs(getPitch()); // We currently ignore negative pitch. 672 | if (color == Color.WHITE && pixelMode == FT_PIXEL_MODE_GRAY && rowBytes == width && gamma == 1) { 673 | pixmap = new FreeTypePixmap(width, rows, Format.Alpha); 674 | changedPixels = pixmap.getRealPixels(); 675 | BufferUtils.copy(src, changedPixels, changedPixels.capacity()); 676 | } else { 677 | pixmap = new FreeTypePixmap(width, rows, Format.RGBA8888); 678 | int rgba = Color.rgba8888(color); 679 | byte[] srcRow = new byte[rowBytes]; 680 | int[] dstRow = new int[width]; 681 | changedPixels = pixmap.getRealPixels(); 682 | IntBuffer dst = changedPixels.asIntBuffer(); 683 | if (pixelMode == FT_PIXEL_MODE_MONO) { 684 | // Use the specified color for each set bit. 685 | for (int y = 0; y < rows; y++) { 686 | src.get(srcRow); 687 | for (int i = 0, x = 0; x < width; i++, x += 8) { 688 | byte b = srcRow[i]; 689 | for (int ii = 0, n = Math.min(8, width - x); ii < n; ii++) { 690 | if ((b & (1 << (7 - ii))) != 0) 691 | dstRow[x + ii] = rgba; 692 | else 693 | dstRow[x + ii] = 0; 694 | } 695 | } 696 | dst.put(dstRow); 697 | } 698 | } else { 699 | // Use the specified color for RGB, blend the FreeType bitmap with alpha. 700 | int rgb = rgba & 0xffffff00; 701 | int a = rgba & 0xff; 702 | for (int y = 0; y < rows; y++) { 703 | src.get(srcRow); 704 | for (int x = 0; x < width; x++) { 705 | // Zero raised to any power is always zero. 706 | // 255 (=one) raised to any power is always one. 707 | // We only need Math.pow() when alpha is NOT zero and NOT one. 708 | int alpha = srcRow[x] & 0xff; 709 | if (alpha == 0) 710 | dstRow[x] = rgb; 711 | else if (alpha == 255) 712 | dstRow[x] = rgb | a; 713 | else 714 | dstRow[x] = rgb | (int)(a * (float)Math.pow(alpha / 255f, gamma)); // Inverse gamma. 715 | } 716 | dst.put(dstRow); 717 | } 718 | } 719 | } 720 | 721 | pixmap.putPixelsBack(changedPixels); 722 | pixmap.setPixelsNull(); 723 | 724 | Pixmap converted = pixmap; 725 | if (format != pixmap.getFormat()) { 726 | converted = new Pixmap(pixmap.getWidth(), pixmap.getHeight(), format); 727 | converted.setBlending(Blending.None); 728 | converted.drawPixmap(pixmap, 0, 0); 729 | converted.setBlending(Blending.SourceOver); 730 | pixmap.dispose(); 731 | } 732 | return converted; 733 | } 734 | // @off 735 | 736 | public int getNumGray() { 737 | return getNumGray(address); 738 | } 739 | 740 | private static native int getNumGray (int bitmap)/*-{ 741 | return $wnd.Module._c_Bitmap_getNumGray(bitmap); 742 | }-*/; 743 | 744 | public int getPixelMode() { 745 | return getPixelMode(address); 746 | } 747 | 748 | private static native int getPixelMode (int bitmap)/*-{ 749 | return $wnd.Module._c_Bitmap_getPixelMode(bitmap); 750 | }-*/; 751 | } 752 | 753 | public static class GlyphMetrics extends Pointer { 754 | GlyphMetrics (int address) { 755 | super(address); 756 | } 757 | 758 | public int getWidth() { 759 | return getWidth(address); 760 | } 761 | 762 | private static native int getWidth (int metrics)/*-{ 763 | return $wnd.Module._c_GlyphMetrics_getWidth(metrics); 764 | }-*/; 765 | 766 | public int getHeight() { 767 | return getHeight(address); 768 | } 769 | 770 | private static native int getHeight (int metrics)/*-{ 771 | return $wnd.Module._c_GlyphMetrics_getHeight(metrics); 772 | }-*/; 773 | 774 | public int getHoriBearingX() { 775 | return getHoriBearingX(address); 776 | } 777 | 778 | private static native int getHoriBearingX (int metrics)/*-{ 779 | return $wnd.Module._c_GlyphMetrics_getHoriBearingX(metrics); 780 | }-*/; 781 | 782 | public int getHoriBearingY() { 783 | return getHoriBearingY(address); 784 | } 785 | 786 | private static native int getHoriBearingY (int metrics)/*-{ 787 | return $wnd.Module._c_GlyphMetrics_getHoriBearingY(metrics); 788 | }-*/; 789 | 790 | public int getHoriAdvance() { 791 | return getHoriAdvance(address); 792 | } 793 | 794 | private static native int getHoriAdvance (int metrics)/*-{ 795 | return $wnd.Module._c_GlyphMetrics_getHoriAdvance(metrics); 796 | }-*/; 797 | 798 | public int getVertBearingX() { 799 | return getVertBearingX(address); 800 | } 801 | 802 | private static native int getVertBearingX (int metrics)/*-{ 803 | return $wnd.Module._c_GlyphMetrics_getVertBearingX(metrics); 804 | }-*/; 805 | 806 | public int getVertBearingY() { 807 | return getVertBearingY(address); 808 | } 809 | 810 | private static native int getVertBearingY (int metrics)/*-{ 811 | return $wnd.Module._c_GlyphMetrics_getVertBearingY(metrics); 812 | }-*/; 813 | 814 | public int getVertAdvance() { 815 | return getVertAdvance(address); 816 | } 817 | 818 | private static native int getVertAdvance (int metrics)/*-{ 819 | return $wnd.Module._c_GlyphMetrics_getVertAdvance(metrics); 820 | }-*/; 821 | } 822 | 823 | public static class Stroker extends Pointer implements Disposable { 824 | Stroker(int address) { 825 | super(address); 826 | } 827 | 828 | public void set(int radius, int lineCap, int lineJoin, int miterLimit) { 829 | set(address, radius, lineCap, lineJoin, miterLimit); 830 | } 831 | 832 | private static native void set (int stroker, int radius, int lineCap, int lineJoin, int miterLimit)/*-{ 833 | $wnd.Module._c_Stroker_set(stroker, radius, lineCap, lineJoin, 834 | miterLimit); 835 | }-*/; 836 | 837 | @Override 838 | public void dispose() { 839 | done(address); 840 | } 841 | 842 | private static native void done (int stroker)/*-{ 843 | $wnd.Module._c_Stroker_done(stroker); 844 | }-*/; 845 | } 846 | 847 | public static int FT_PIXEL_MODE_NONE = 0; 848 | public static int FT_PIXEL_MODE_MONO = 1; 849 | public static int FT_PIXEL_MODE_GRAY = 2; 850 | public static int FT_PIXEL_MODE_GRAY2 = 3; 851 | public static int FT_PIXEL_MODE_GRAY4 = 4; 852 | public static int FT_PIXEL_MODE_LCD = 5; 853 | public static int FT_PIXEL_MODE_LCD_V = 6; 854 | 855 | private static int encode (char a, char b, char c, char d) { 856 | return (a << 24) | (b << 16) | (c << 8) | d; 857 | } 858 | 859 | public static int FT_ENCODING_NONE = 0; 860 | public static int FT_ENCODING_MS_SYMBOL = encode('s', 'y', 'm', 'b'); 861 | public static int FT_ENCODING_UNICODE = encode('u', 'n', 'i', 'c'); 862 | public static int FT_ENCODING_SJIS = encode('s', 'j', 'i', 's'); 863 | public static int FT_ENCODING_GB2312 = encode('g', 'b', ' ', ' '); 864 | public static int FT_ENCODING_BIG5 = encode('b', 'i', 'g', '5'); 865 | public static int FT_ENCODING_WANSUNG = encode('w', 'a', 'n', 's'); 866 | public static int FT_ENCODING_JOHAB = encode('j', 'o', 'h', 'a'); 867 | public static int FT_ENCODING_ADOBE_STANDARD = encode('A', 'D', 'O', 'B'); 868 | public static int FT_ENCODING_ADOBE_EXPERT = encode('A', 'D', 'B', 'E'); 869 | public static int FT_ENCODING_ADOBE_CUSTOM = encode('A', 'D', 'B', 'C'); 870 | public static int FT_ENCODING_ADOBE_LATIN_1 = encode('l', 'a', 't', '1'); 871 | public static int FT_ENCODING_OLD_LATIN_2 = encode('l', 'a', 't', '2'); 872 | public static int FT_ENCODING_APPLE_ROMAN = encode('a', 'r', 'm', 'n'); 873 | 874 | public static int FT_FACE_FLAG_SCALABLE = ( 1 << 0 ); 875 | public static int FT_FACE_FLAG_FIXED_SIZES = ( 1 << 1 ); 876 | public static int FT_FACE_FLAG_FIXED_WIDTH = ( 1 << 2 ); 877 | public static int FT_FACE_FLAG_SFNT = ( 1 << 3 ); 878 | public static int FT_FACE_FLAG_HORIZONTAL = ( 1 << 4 ); 879 | public static int FT_FACE_FLAG_VERTICAL = ( 1 << 5 ); 880 | public static int FT_FACE_FLAG_KERNING = ( 1 << 6 ); 881 | public static int FT_FACE_FLAG_FAST_GLYPHS = ( 1 << 7 ); 882 | public static int FT_FACE_FLAG_MULTIPLE_MASTERS = ( 1 << 8 ); 883 | public static int FT_FACE_FLAG_GLYPH_NAMES = ( 1 << 9 ); 884 | public static int FT_FACE_FLAG_EXTERNAL_STREAM = ( 1 << 10 ); 885 | public static int FT_FACE_FLAG_HINTER = ( 1 << 11 ); 886 | public static int FT_FACE_FLAG_CID_KEYED = ( 1 << 12 ); 887 | public static int FT_FACE_FLAG_TRICKY = ( 1 << 13 ); 888 | 889 | public static int FT_STYLE_FLAG_ITALIC = ( 1 << 0 ); 890 | public static int FT_STYLE_FLAG_BOLD = ( 1 << 1 ); 891 | 892 | public static int FT_LOAD_DEFAULT = 0x0; 893 | public static int FT_LOAD_NO_SCALE = 0x1; 894 | public static int FT_LOAD_NO_HINTING = 0x2; 895 | public static int FT_LOAD_RENDER = 0x4; 896 | public static int FT_LOAD_NO_BITMAP = 0x8; 897 | public static int FT_LOAD_VERTICAL_LAYOUT = 0x10; 898 | public static int FT_LOAD_FORCE_AUTOHINT = 0x20; 899 | public static int FT_LOAD_CROP_BITMAP = 0x40; 900 | public static int FT_LOAD_PEDANTIC = 0x80; 901 | public static int FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH = 0x200; 902 | public static int FT_LOAD_NO_RECURSE = 0x400; 903 | public static int FT_LOAD_IGNORE_TRANSFORM = 0x800; 904 | public static int FT_LOAD_MONOCHROME = 0x1000; 905 | public static int FT_LOAD_LINEAR_DESIGN = 0x2000; 906 | public static int FT_LOAD_NO_AUTOHINT = 0x8000; 907 | 908 | public static int FT_LOAD_TARGET_NORMAL = 0x0; 909 | public static int FT_LOAD_TARGET_LIGHT = 0x10000; 910 | public static int FT_LOAD_TARGET_MONO = 0x20000; 911 | public static int FT_LOAD_TARGET_LCD = 0x30000; 912 | public static int FT_LOAD_TARGET_LCD_V = 0x40000; 913 | 914 | public static int FT_RENDER_MODE_NORMAL = 0; 915 | public static int FT_RENDER_MODE_LIGHT = 1; 916 | public static int FT_RENDER_MODE_MONO = 2; 917 | public static int FT_RENDER_MODE_LCD = 3; 918 | public static int FT_RENDER_MODE_LCD_V = 4; 919 | public static int FT_RENDER_MODE_MAX = 5; 920 | 921 | public static int FT_KERNING_DEFAULT = 0; 922 | public static int FT_KERNING_UNFITTED = 1; 923 | public static int FT_KERNING_UNSCALED = 2; 924 | 925 | public static int FT_STROKER_LINECAP_BUTT = 0; 926 | public static int FT_STROKER_LINECAP_ROUND = 1; 927 | public static int FT_STROKER_LINECAP_SQUARE = 2; 928 | 929 | public static int FT_STROKER_LINEJOIN_ROUND = 0; 930 | public static int FT_STROKER_LINEJOIN_BEVEL = 1; 931 | public static int FT_STROKER_LINEJOIN_MITER_VARIABLE = 2; 932 | public static int FT_STROKER_LINEJOIN_MITER = FT_STROKER_LINEJOIN_MITER_VARIABLE; 933 | public static int FT_STROKER_LINEJOIN_MITER_FIXED = 3; 934 | 935 | public static Library initFreeType() { 936 | int address = initFreeTypeJni(); 937 | if(address == 0) 938 | throw new GdxRuntimeException("Couldn't initialize FreeType library, FreeType error code: " + getLastErrorCode()); 939 | else 940 | return new Library(address); 941 | } 942 | 943 | private static native int initFreeTypeJni ()/*-{ 944 | return $wnd.Module._c_FreeType_initFreeTypeJni(); 945 | }-*/; 946 | 947 | public static int toInt (int value) { 948 | return ((value + 63) & -64) >> 6; 949 | } 950 | 951 | // public static void main (String[] args) throws Exception { 952 | // FreetypeBuild.main(args); 953 | // new SharedLibraryLoader("libs/gdx-freetype-natives.jar").load("gdx-freetype"); 954 | // String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*€�?‚ƒ„…†‡ˆ‰Š‹Œ�?Ž�?�?‘’“”•–—˜™š›œ�?žŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿À�?ÂÃÄÅÆÇÈÉÊËÌ�?Î�?�?ÑÒÓÔÕÖרÙÚÛÜ�?Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ"; 955 | // 956 | // Library library = FreeType.initFreeType(); 957 | // Face face = library.newFace(new FileHandle("arial.ttf"), 0); 958 | // face.setPixelSizes(0, 15); 959 | // SizeMetrics faceMetrics = face.getSize().getMetrics(); 960 | // System.out.println(toInt(faceMetrics.getAscender()) + ", " + toInt(faceMetrics.getDescender()) + ", " + toInt(faceMetrics.getHeight())); 961 | // 962 | // for(int i = 0; i < chars.length(); i++) { 963 | // if(!FreeType.loadGlyph(face, FreeType.getCharIndex(face, chars.charAt(i)), 0)) continue; 964 | // if(!FreeType.renderGlyph(face.getGlyph(), FT_RENDER_MODE_NORMAL)) continue; 965 | // Bitmap bitmap = face.getGlyph().getBitmap(); 966 | // GlyphMetrics glyphMetrics = face.getGlyph().getMetrics(); 967 | // System.out.println(toInt(glyphMetrics.getHoriBearingX()) + ", " + toInt(glyphMetrics.getHoriBearingY())); 968 | // System.out.println(toInt(glyphMetrics.getWidth()) + ", " + toInt(glyphMetrics.getHeight()) + ", " + toInt(glyphMetrics.getHoriAdvance())); 969 | // System.out.println(bitmap.getWidth() + ", " + bitmap.getRows() + ", " + bitmap.getPitch() + ", " + bitmap.getNumGray()); 970 | // for(int y = 0; y < bitmap.getRows(); y++) { 971 | // for(int x = 0; x < bitmap.getWidth(); x++) { 972 | // System.out.print(bitmap.getBuffer().get(x + bitmap.getPitch() * y) != 0? "X": " "); 973 | // } 974 | // System.out.println(); 975 | // } 976 | // } 977 | // 978 | // face.dispose(); 979 | // library.dispose(); 980 | // } 981 | } 982 | --------------------------------------------------------------------------------