/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016–2017, 2019–2023 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.media.graphics;
import java.io.*;
import java.util.*;
import malik.emulator.fileformats.font.*;
import malik.emulator.util.*;
public final class SystemFont extends Object
{
private static class Enumerator extends Object implements Enumeration
{
public SystemFont current;
public Enumerator(SystemFont first) {
this.current = first;
}
public boolean hasMoreElements() {
return current != null;
}
public Object nextElement() {
SystemFont result;
if((result = current) == null)
{
throw new NoSuchElementException("Enumeration.nextElement: элементов больше не осталось.");
}
current = result.getNext();
return result;
}
}
public static final int STYLE_BOLD = 0x01;
public static final int STYLE_ITALIC = 0x02;
private static final SystemFont FIRST_SYSTEM;
private static final SystemFont LAST_SYSTEM;
private static final Object MONITOR;
static {
SystemFont last = null;
SystemFont first = null;
for(int handle = findNextHandle(0); handle != 0; handle = findNextHandle(handle))
{
SystemFont font = new SystemFont(handle, true);
last = last != null ? ((font.prev = last).next = font) : (first = font);
}
FIRST_SYSTEM = first;
LAST_SYSTEM = last;
MONITOR = new Object();
}
public static Enumeration systemFonts() {
final SystemFont last = LAST_SYSTEM;
return new Enumerator(FIRST_SYSTEM) {
public Object nextElement() {
SystemFont result;
if((result = current) == null)
{
throw new NoSuchElementException("Enumeration.nextElement: элементов больше не осталось.");
}
current = result == last ? null : result.getNext();
return result;
}
};
}
public static Enumeration installedFonts() {
return new Enumerator(LAST_SYSTEM.next);
}
public static Enumeration availableFonts() {
return new Enumerator(FIRST_SYSTEM);
}
public static SystemFont get(String fontName) {
for(SystemFont font = FIRST_SYSTEM; font != null; font = font.next) if(fontName == null ? font.name == null : fontName.equals(font.name)) return font;
return FIRST_SYSTEM;
}
public static SystemFont getDefault() {
return FIRST_SYSTEM;
}
public static SystemFont install(String fontName, String fileName) throws IOException {
int len;
int handle;
char[] nameOfFile;
char[] nameOfFont;
SystemFont result;
SystemFont sfont1;
SystemFont sfont2;
if(fileName == null)
{
throw new NullPointerException("SystemFont.install: аргумент fileName равен нулевой ссылке.");
}
if(fontName == null)
{
int pos1 = fileName.lastIndexOf('/') + 1;
int pos2 = fileName.lastIndexOf('.');
fontName = pos1 <= pos2 ? fileName.substring(pos1, pos2) : fileName.substring(pos1);
}
fileName.getChars(0, len = fileName.length(), nameOfFile = new char[len + 1], 0);
fontName.getChars(0, len = fontName.length(), nameOfFont = new char[len + 1], 0);
if((handle = (int) MalikSystem.syscall(Array.getFirstElementAddress(nameOfFont), Array.getFirstElementAddress(nameOfFile), 0x002e)) == 0)
{
throw new IOException((new StringBuilder()).append("SystemFont.install: ошибка установки шрифта из ").append(fileName).append('.').toString());
}
result = new SystemFont(handle, false);
synchronized(MONITOR)
{
sfont2 = (sfont1 = LAST_SYSTEM).next;
result.prev = sfont1;
result.next = sfont2;
sfont1.next = result;
if(sfont2 != null) sfont2.prev = result;
}
return result;
}
public static SystemFont install(String fontName, UnicodeRasterFont font) throws IOException {
String fileName;
if(font == null)
{
throw new NullPointerException("SystemFont.install: аргумент font равен нулевой ссылке.");
}
if((fileName = font.getLastFileName()) == null)
{
throw new IllegalStateException("SystemFont.install: объект font не содержит имени файла.");
}
return install(fontName, fileName);
}
static Object monitor() {
return MONITOR;
}
private static int findNextHandle(int handle) {
return (int) MalikSystem.syscall((long) handle, 0x0028);
}
private boolean installed;
private final boolean system;
private final int style;
private final int handle;
private final int height;
private final int baselineHeight;
private final int baselinePosition;
private final StringDrawDescriptor descriptor;
private final String name;
private SystemFont prev;
private SystemFont next;
private SystemFont(int handle, boolean system) {
int h;
int b;
int s;
char[] name;
s = (int) MalikSystem.syscall((long) handle, 0x002a);
MalikSystem.syscall(handle, Array.getFirstElementAddress(name = new char[(int) MalikSystem.syscall(handle, 0, 0x0029)]), 0x0029);
this.installed = true;
this.system = system;
this.style = s & (STYLE_BOLD | STYLE_ITALIC);
this.handle = handle;
this.height = h = (s >> 8) & 0xff;
this.baselineHeight = b = (s >> 16) & 0xff;
this.baselinePosition = h - b;
this.descriptor = new StringDrawDescriptor();
this.name = new String(name);
}
public String toString() {
int s = style;
return (new StringBuilder()).
append("Системный шрифт[\"").append(name).append("\", размер=").append(height).
append((s & STYLE_BOLD) != 0 ? ", жирный" : "").append((s & STYLE_ITALIC) != 0 ? ", курсив" : "").append("]").
toString();
}
public void uninstall() {
if(system)
{
throw new IllegalStateException("SystemFont.uninstall: нельзя удалить системный шрифт.");
}
synchronized(MONITOR)
{
if(installed)
{
SystemFont sfont1;
SystemFont sfont2;
MalikSystem.syscall((long) handle, 0x002f);
installed = false;
sfont1 = prev;
sfont2 = next;
prev = next = null;
sfont1.next = sfont2;
if(sfont2 != null) sfont2.prev = sfont1;
}
}
}
public boolean isInstalled() {
return installed;
}
public boolean isSystem() {
return system;
}
public boolean isBold() {
return (style & STYLE_BOLD) != 0;
}
public boolean isItalic() {
return (style & STYLE_ITALIC) != 0;
}
public boolean characterSupported(int charCode) {
boolean result;
int error;
if(system) return (int) MalikSystem.syscall(handle, charCode, 0x002b) != 0;
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = false;
break label0;
}
result = (int) MalikSystem.syscall(handle, charCode, 0x002b) != 0;
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.characterSupported: этот шрифт был удалён из системы.");
}
return result;
}
public int characterWidth(int charCode) {
int error;
int result;
StringDrawDescriptor d;
if(system)
{
synchronized(d = descriptor)
{
d.assignString(false, MalikSystem.getLocalVariableAddress(charCode), 1, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
return result;
}
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = 0;
break label0;
}
synchronized(d = descriptor)
{
d.assignString(false, MalikSystem.getLocalVariableAddress(charCode), 1, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.characterWidth: этот шрифт был удалён из системы.");
}
return result;
}
public int charactersWidth(char[] src) {
int error;
int result;
StringDrawDescriptor d;
if(src == null)
{
throw new NullPointerException("SystemFont.charactersWidth: аргумент src равен нулевой ссылке.");
}
if(system)
{
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(src), src.length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
return result;
}
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = 0;
break label0;
}
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(src), src.length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.charactersWidth: этот шрифт был удалён из системы.");
}
return result;
}
public int charactersWidth(char[] src, int offset, int length) {
int error;
int result;
StringDrawDescriptor d;
if(src == null)
{
throw new NullPointerException("SystemFont.charactersWidth: аргумент src равен нулевой ссылке.");
}
Array.checkBound("SystemFont.charactersWidth", src.length, offset, length);
if(system)
{
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(src) + (offset << 1), length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
return result;
}
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = 0;
break label0;
}
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(src) + (offset << 1), length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.charactersWidth: этот шрифт был удалён из системы.");
}
return result;
}
public int charactersWidth(int[] src) {
int error;
int result;
StringDrawDescriptor d;
if(src == null)
{
throw new NullPointerException("SystemFont.charactersWidth: аргумент src равен нулевой ссылке.");
}
if(system)
{
synchronized(d = descriptor)
{
d.assignString(false, Array.getFirstElementAddress(src), src.length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
return result;
}
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = 0;
break label0;
}
synchronized(d = descriptor)
{
d.assignString(false, Array.getFirstElementAddress(src), src.length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.charactersWidth: этот шрифт был удалён из системы.");
}
return result;
}
public int charactersWidth(int[] src, int offset, int length) {
int error;
int result;
StringDrawDescriptor d;
if(src == null)
{
throw new NullPointerException("SystemFont.charactersWidth: аргумент src равен нулевой ссылке.");
}
Array.checkBound("SystemFont.charactersWidth", src.length, offset, length);
if(system)
{
synchronized(d = descriptor)
{
d.assignString(false, Array.getFirstElementAddress(src) + (offset << 2), length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
return result;
}
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = 0;
break label0;
}
synchronized(d = descriptor)
{
d.assignString(false, Array.getFirstElementAddress(src) + (offset << 2), length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.charactersWidth: этот шрифт был удалён из системы.");
}
return result;
}
public int stringWidth(String string) {
int error;
int result;
StringDrawDescriptor d;
if(string == null)
{
throw new NullPointerException("SystemFont.stringWidth: аргумент string равен нулевой ссылке.");
}
if(system)
{
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(string), string.length(), handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
return result;
}
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = 0;
break label0;
}
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(string), string.length(), handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.stringWidth: этот шрифт был удалён из системы.");
}
return result;
}
public int substringWidth(String string, int offset, int length) {
int error;
int result;
StringDrawDescriptor d;
if(string == null)
{
throw new NullPointerException("SystemFont.substringWidth: аргумент string равен нулевой ссылке.");
}
String.checkBound("SystemFont.substringWidth", string.length(), offset, length);
if(system)
{
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(string) + (offset << 1), length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
return result;
}
error = 0;
synchronized(MONITOR)
{
label0:
{
if(!installed)
{
error = 1;
result = 0;
break label0;
}
synchronized(d = descriptor)
{
d.assignString(true, Array.getFirstElementAddress(string) + (offset << 1), length, handle);
result = (int) MalikSystem.syscall((long) d.getDescriptorAddress(), 0x002c);
}
}
}
if(error == 1)
{
throw new UninstalledFontException("SystemFont.substringWidth: этот шрифт был удалён из системы.");
}
return result;
}
public int getHeight() {
return height;
}
public int getBaselineHeight() {
return baselineHeight;
}
public int getBaselinePosition() {
return baselinePosition;
}
public int getStyle() {
return style;
}
public String getName() {
return name;
}
int getHandle() {
return handle;
}
SystemFont getNext() {
return next;
}
}