Skip to content

Commit

Permalink
TTF
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiao-MoMi committed Oct 6, 2024
1 parent 4b2ddf6 commit 2ba7ad9
Show file tree
Hide file tree
Showing 15 changed files with 561 additions and 49 deletions.
1 change: 0 additions & 1 deletion api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ dependencies {
tasks {
shadowJar {
archiveClassifier = ""
archiveFileName = "CustomNameplates-${rootProject.properties["project_version"]}.jar"
relocate ("net.kyori", "net.momirealms.customnameplates.libraries")
relocate("dev.dejvokep", "net.momirealms.customnameplates.libraries")
}
Expand Down
5 changes: 3 additions & 2 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
compileOnly("com.github.ben-manes.caffeine:caffeine:${rootProject.properties["caffeine_version"]}")
// COMMONS IO
compileOnly("commons-io:commons-io:${rootProject.properties["commons_io_version"]}")
// lwjgl
implementation("org.lwjgl:lwjgl-freetype:3.3.4")
// TTF
compileOnly("org.lwjgl:lwjgl-freetype:${rootProject.properties["lwjgl_version"]}")
compileOnly("org.lwjgl:lwjgl:${rootProject.properties["lwjgl_version"]}")
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,23 @@
import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder;
import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder;
import net.momirealms.customnameplates.api.util.CharacterUtils;
import net.momirealms.customnameplates.backend.util.FreeTypeUtils;
import net.momirealms.customnameplates.common.util.Tuple;
import org.apache.commons.io.FileUtils;
import org.jetbrains.annotations.Nullable;
import org.lwjgl.PointerBuffer;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.util.freetype.FT_Face;
import org.lwjgl.util.freetype.FT_GlyphSlot;
import org.lwjgl.util.freetype.FT_Vector;
import org.lwjgl.util.freetype.FreeType;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
Expand Down Expand Up @@ -768,16 +778,115 @@ private void init() {

File ttfCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp");
if (!ttfCache.exists()) {
File ttfFile = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path);
if (!ttfFile.exists()) {
plugin.getPluginLogger().warn(ttfFile.getAbsolutePath() + " not found");
File ttf = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path);
if (!ttf.exists()) {
plugin.getPluginLogger().warn(ttf.getAbsolutePath() + " not found");
return;
}
if (!ttfFile.getName().endsWith(".ttf")) {
plugin.getPluginLogger().warn(ttfFile.getAbsolutePath() + " is not a .ttf");
if (!ttf.getName().endsWith(".ttf")) {
plugin.getPluginLogger().warn(ttf.getAbsolutePath() + " is not a .ttf");
return;
}
try (InputStream inputStream = new FileInputStream(ttf)) {
ByteBuffer byteBuffer = null;
FT_Face fT_Face = null;
try {
ttfCache.getParentFile().mkdirs();
ttfCache.createNewFile();
YamlDocument yml = plugin.getConfigManager().loadData(ttfCache);
byteBuffer = FreeTypeUtils.readResource(inputStream);
byteBuffer.flip();
synchronized(FreeTypeUtils.LOCK) {
MemoryStack ms1 = MemoryStack.stackPush();
try {
PointerBuffer pointerBuffer = ms1.mallocPointer(1);
FreeTypeUtils.checkFatalError(FreeType.FT_New_Memory_Face(FreeTypeUtils.initialize(), byteBuffer, 0L, pointerBuffer), "Initializing font face");
fT_Face = FT_Face.create(pointerBuffer.get());
} catch (Throwable t1) {
try {
ms1.close();
} catch (Throwable t2) {
t1.addSuppressed(t2);
}
throw t1;
}
ms1.close();

String string = FreeType.FT_Get_Font_Format(fT_Face);
if (!"TrueType".equals(string)) {
throw new IOException("Font is not in TTF format, was " + string);
}

FreeTypeUtils.checkFatalError(FreeType.FT_Select_Charmap(fT_Face, FreeType.FT_ENCODING_UNICODE), "Find unicode charmap");
Set<Integer> codePoints = new HashSet<>(1_000);

int pixelWidth = Math.round(size * oversample);
int pixelHeight = Math.round(size * oversample);
FreeType.FT_Set_Pixel_Sizes(fT_Face, pixelWidth, pixelHeight);

MemoryStack ms2 = MemoryStack.stackPush();
try {
FT_Vector fT_Vector = FreeTypeUtils.setShift(FT_Vector.malloc(ms2), 0, 0);
FreeType.FT_Set_Transform(fT_Face, null, fT_Vector);
} catch (Throwable t1) {
try {
ms2.close();
} catch (Throwable t2) {
t1.addSuppressed(t2);
}
throw t1;
}
ms2.close();

MemoryStack ms3 = MemoryStack.stackPush();
try {
IntBuffer intBuffer = ms3.mallocInt(1);
for (long l = FreeType.FT_Get_First_Char(fT_Face, intBuffer); intBuffer.get(0) != 0; l = FreeType.FT_Get_Next_Char(fT_Face, l, intBuffer)) {
codePoints.add((int) l);
}
} catch (Throwable t1) {
try {
ms3.close();
} catch (Throwable t2) {
t1.addSuppressed(t2);
}
throw t1;
}
ms3.close();

for (int skippedCodePoint : skippCodePoints) {
codePoints.remove(skippedCodePoint);
}
for (int codePoint : codePoints) {
int i = FreeType.FT_Get_Char_Index(fT_Face, codePoint);
if (i != 0) {
FreeTypeUtils.checkFatalError(FreeType.FT_Load_Glyph(fT_Face, i, 4194312), "Loading glyph");
FT_GlyphSlot fT_GlyphSlot = Objects.requireNonNull(fT_Face.glyph(), "Glyph not initialized");
float advance = FreeTypeUtils.getAdvance(fT_GlyphSlot.advance());
char[] theCharacter = Character.toChars(codePoint);
String unicode = CharacterUtils.char2Unicode(theCharacter);
yml.set(unicode, (advance) / oversample);
}
}
}

yml.save(ttfCache);
} catch (Exception e) {
synchronized(FreeTypeUtils.LOCK) {
if (fT_Face != null) {
FreeType.FT_Done_Face(fT_Face);
}
}
MemoryUtil.memFree(byteBuffer);
throw e;
}

// free resources
MemoryUtil.memFree(byteBuffer);

} catch (IOException e) {
throw new RuntimeException(e);
}
}

registerCharacterFontData(id, ttfCache, (properties) -> {
Expand Down Expand Up @@ -916,6 +1025,7 @@ public void unload() {
public void disable() {
this.unload();
this.charFontWidthDataMap.clear();
FreeTypeUtils.release();
}

private void loadTemplates() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright (C) <2022> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package net.momirealms.customnameplates.backend.util;

import org.lwjgl.PointerBuffer;
import org.lwjgl.system.MemoryStack;
import org.lwjgl.system.MemoryUtil;
import org.lwjgl.util.freetype.FT_Vector;
import org.lwjgl.util.freetype.FreeType;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;

public class FreeTypeUtils {

public static final Object LOCK = new Object();
private static long freeType = 0L;

public FreeTypeUtils() {
}

public static long initialize() {
synchronized(LOCK) {
if (freeType == 0L) {
MemoryStack memoryStack = MemoryStack.stackPush();
try {
PointerBuffer pointerBuffer = memoryStack.mallocPointer(1);
checkFatalError(FreeType.FT_Init_FreeType(pointerBuffer), "Initializing FreeType library");
freeType = pointerBuffer.get();
} catch (Throwable t1) {
try {
memoryStack.close();
} catch (Throwable t2) {
t1.addSuppressed(t2);
}
throw t1;
}
memoryStack.close();
}
return freeType;
}
}

public static void checkFatalError(int code, String description) {
if (code != 0) {
String var10002 = getErrorMessage(code);
throw new IllegalStateException("FreeType error: " + var10002 + " (" + description + ")");
}
}

private static String getErrorMessage(int code) {
String string = FreeType.FT_Error_String(code);
return string != null ? string : "Unrecognized error: 0x" + Integer.toHexString(code);
}

public static FT_Vector setShift(FT_Vector vec, float x, float y) {
long xShift = Math.round(x * 64.0F);
long yShift = Math.round(y * 64.0F);
return vec.set(xShift, yShift);
}

public static float getAdvance(FT_Vector vec) {
return (float) vec.x() / 64.0F;
}

public static void release() {
synchronized(LOCK) {
if (freeType != 0L) {
FreeType.FT_Done_Library(freeType);
freeType = 0L;
}
}
}

public static ByteBuffer readResource(InputStream inputStream) throws IOException {
ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream);
if (readableByteChannel instanceof SeekableByteChannel seekableByteChannel) {
return readResource(readableByteChannel, (int)seekableByteChannel.size() + 1);
} else {
return readResource(readableByteChannel, 8192);
}
}

private static ByteBuffer readResource(ReadableByteChannel channel, int bufSize) throws IOException {
ByteBuffer byteBuffer = MemoryUtil.memAlloc(bufSize);
try {
while(channel.read(byteBuffer) != -1) {
if (!byteBuffer.hasRemaining()) {
byteBuffer = MemoryUtil.memRealloc(byteBuffer, byteBuffer.capacity() * 2);
}
}
return byteBuffer;
} catch (IOException e) {
MemoryUtil.memFree(byteBuffer);
throw e;
}
}
}
3 changes: 1 addition & 2 deletions bukkit/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ dependencies {

tasks {
shadowJar {
archiveFileName = "CustomNameplates-bukkit-${rootProject.properties["project_version"]}.jar"
archiveFileName = "CustomNameplates-Bukkit-${rootProject.properties["project_version"]}.jar"
destinationDirectory.set(file("$rootDir/target"))
relocate("net.kyori", "net.momirealms.customnameplates.libraries")
relocate("org.incendo", "net.momirealms.customnameplates.libraries")
Expand All @@ -62,7 +62,6 @@ tasks {
relocate("com.github.benmanes.caffeine", "net.momirealms.customnameplates.libraries.caffeine")
relocate("net.objecthunter.exp4j", "net.momirealms.customnameplates.libraries.exp4j")
relocate("redis.clients.jedis", "net.momirealms.customnameplates.libraries.jedis")
relocate("org.apache.commons.io", "net.momirealms.customnameplates.libraries.commons.io")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import net.momirealms.customnameplates.backend.storage.StorageManagerImpl;
import net.momirealms.customnameplates.bukkit.command.BukkitCommandManager;
import net.momirealms.customnameplates.bukkit.compatibility.NameplatesExpansion;
import net.momirealms.customnameplates.bukkit.compatibility.cosmetic.MagicCosmeticsHook;
import net.momirealms.customnameplates.bukkit.requirement.BukkitRequirementManager;
import net.momirealms.customnameplates.bukkit.scheduler.BukkitSchedulerAdapter;
import net.momirealms.customnameplates.common.dependency.Dependency;
Expand Down Expand Up @@ -116,7 +117,8 @@ public void load() {
Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON,
Dependency.HIKARI_CP,
Dependency.BYTE_BUDDY,
Dependency.COMMONS_IO
Dependency.COMMONS_IO,
Dependency.LWJGL, Dependency.LWJGL_NATIVES, Dependency.LWJGL_FREETYPE, Dependency.LWJGL_FREETYPE_NATIVES
)
);
}
Expand Down Expand Up @@ -173,6 +175,12 @@ public void enable() {
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
new NameplatesExpansion(this).register();
}
if (Bukkit.getPluginManager().isPluginEnabled("MagicCosmetics")) {
try {
Bukkit.getPluginManager().registerEvents(new MagicCosmeticsHook(this), this.getBootstrap());
} catch (Exception ignore) {
}
}

boolean downloadFromPolymart = polymart.equals("1");
boolean downloadFromBBB = buildByBit.equals("true");
Expand Down
Loading

0 comments on commit 2ba7ad9

Please sign in to comment.