/*
Реализация спецификаций 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.fileformats.font;
import java.io.*;
import java.util.*;
import malik.emulator.fileformats.*;
import malik.emulator.io.cloud.*;
import malik.emulator.util.*;
public final class UnicodeRasterFont extends Object implements DataCodec
{
static final int MAX_SIGNED_VALUE = +0x7f;
static final int MIN_SIGNED_VALUE = -0x80;
static final int MAX_UNSIGNED_VALUE = 0xff;
static final int MIN_UNSIGNED_VALUE = 0x00;
public static final int MAX_COORDINATE = +0x7f;
public static final int MIN_COORDINATE = -0x7f;
public static final long SIGNATURE = 0x55464e54L;
static boolean isEmptyCol(int width, int height, byte[] pixels, int col) {
for(int scan = bytesNeeded(width), mask = 1 << (col & 7), i = col >> 3, row = height; row-- > 0; i += scan) if((pixels[i] & mask) != 0) return false;
return true;
}
static boolean isEmptyRow(int width, int height, byte[] pixels, int row) {
for(int scan = bytesNeeded(width), mend = maskEnding(width), base = scan * row, c = scan + base - 1, i = base; i <= c; i++)
{
int mask = i < c ? 0xff : mend;
if((pixels[i] & mask) != 0) return false;
}
return true;
}
static boolean isEmptyGlyph(int width, int height, byte[] pixels) {
for(int scan = bytesNeeded(width), mend = maskEnding(width), len = scan * height, c = scan - 1, i = 0; i < len; i++)
{
int mask;
if(i < c)
{
mask = 0xff;
} else
{
mask = mend;
c += scan;
}
if((pixels[i] & mask) != 0) return false;
}
return true;
}
static boolean pixelsMatches(int width, int height, byte[] pixels1, byte[] pixels2) {
for(int scan = bytesNeeded(width), mend = maskEnding(width), len = scan * height, c = scan - 1, i = 0; i < len; i++)
{
int mask;
if(i < c)
{
mask = 0xff;
} else
{
mask = mend;
c += scan;
}
if((pixels1[i] & mask) != (pixels2[i] & mask)) return false;
}
return true;
}
static int pixelsHashCode(int width, int height, byte[] pixels) {
int result = 0;
for(int scan = bytesNeeded(width), mend = maskEnding(width), len = scan * height, c = scan - 1, i = 0; i < len; i++)
{
int mask;
if(i < c)
{
mask = 0xff;
} else
{
mask = mend;
c += scan;
}
result ^= (pixels[i] & mask) << (i << 3);
}
return result;
}
static int madeRectangle(int left, int bottom, int width, int height) {
return left << 24 | (bottom & 0xff) << 16 | (width & 0xff) << 8 | (height & 0xff);
}
static int bytesNeeded(int width, int height) {
return (width + (-width & 7) >> 3) * height;
}
static int bytesNeeded(int width) {
return width + (-width & 7) >> 3;
}
static int toCanonical(int rectangle) {
return (rectangle & 0xff00) == 0 || (rectangle & 0x00ff) == 0 ? 0 : rectangle;
}
static int maskEnding(int width) {
return (width &= 7) > 0 ? (1 << width) - 1 : 0xff;
}
static int indexOf(int characterCode, Glyph[] glyphs, int count) {
for(int result, limit1 = 0, limit2 = count - 1; limit1 <= limit2; )
{
int guess;
if((guess = glyphs[result = (limit1 + limit2) >> 1].getCharacterCode() - characterCode) < 0)
{
limit1 = result + 1;
}
else if(guess > 0)
{
limit2 = result - 1;
}
else
{
return result;
}
}
return -1;
}
static int indexOf(Glyph glyph, Glyph[] glyphs, int count) {
return Array.findb(glyphs, count - 1, glyph);
}
public class Glyph extends Object implements DataCodec
{
boolean removed;
int characterCode;
int offset;
int width;
int rectangle;
byte[] pixels;
Glyph(boolean removed, int characterCode, int width, int rectangle, byte[] pixels) {
this.removed = removed;
this.characterCode = characterCode;
this.width = width;
this.rectangle = rectangle;
this.pixels = pixels;
}
public boolean equals(Object anot) {
boolean result;
if(anot == this) return true;
if(!(anot instanceof Glyph)) return false;
synchronized((UnicodeRasterFont.this).monitor)
{
Glyph g;
synchronized((g = (Glyph) anot).monitor())
{
int r;
result = width == g.width && (r = rectangle) == g.rectangle && pixelsMatches(r >> 8 & 0xff, r & 0xff, pixels, g.pixels);
}
}
return result;
}
public int hashCode() {
int result;
synchronized((UnicodeRasterFont.this).monitor)
{
int r;
result = (width << 12) ^ (r = rectangle) ^ pixelsHashCode(r >> 8 & 0xff, r & 0xff, pixels);
}
return result;
}
public void loadFromInputStream(InputStream stream) throws IOException {
loadFromDataStream(new ExtendedDataInputStream(stream));
}
public void loadFromDataStream(ExtendedDataInputStream stream) throws IOException {
synchronized((UnicodeRasterFont.this).monitor)
{
int r;
width = stream.readUnsignedByte();
rectangle = r = toCanonical(stream.readInt());
stream.readFully(madeLarger(r), 0, bytesNeeded(r >> 8 & 0xff, r & 0xff));
if(r != 0) trim();
}
}
public void saveToOutputStream(OutputStream stream) throws IOException {
saveToDataStream(new ExtendedDataOutputStream(stream));
}
public void saveToDataStream(ExtendedDataOutputStream stream) throws IOException {
synchronized((UnicodeRasterFont.this).monitor)
{
int r;
stream.writeByte(width);
stream.writeInt(r = rectangle);
stream.write(pixels, 0, bytesNeeded(r >> 8 & 0xff, r & 0xff));
}
}
public void clear() {
synchronized((UnicodeRasterFont.this).monitor)
{
width = 0;
rectangle = 0;
}
}
public boolean isEmpty() {
boolean result;
synchronized((UnicodeRasterFont.this).monitor)
{
result = (width | rectangle) == 0;
}
return result;
}
public void paste(Glyph source) {
if(source == null)
{
throw new NullPointerException("Glyph.paste: аргумент source равен нулевой ссылке.");
}
synchronized((UnicodeRasterFont.this).monitor)
{
synchronized(source.monitor())
{
int r;
width = source.width;
rectangle = r = source.rectangle;
Array.copy(source.pixels, 0, madeLarger(r), 0, bytesNeeded(r >> 8 & 0xff, r & 0xff));
}
}
}
public void move(int deltaX, int deltaY) {
if(deltaX == 0 && deltaY == 0) return;
synchronized((UnicodeRasterFont.this).monitor)
{
int r = rectangle;
int maxX = (MAX_COORDINATE + 1) - (r >> 8 & 0xff);
int maxY = (MAX_COORDINATE + 1) - (r & 0xff);
int left = (r >> 24) + deltaX;
int bottom = (byte) (r >> 16) + deltaY;
if(left < MIN_COORDINATE) left = MIN_COORDINATE;
if(left > maxX) left = maxX;
if(bottom < MIN_COORDINATE) bottom = MIN_COORDINATE;
if(bottom > maxY) bottom = maxY;
rectangle = toCanonical(r & 0xffff | left << 24 | (bottom & 0xff) << 16);
}
}
public void putPixel(int x, int y, boolean opaque) {
if(x < MIN_COORDINATE || x > MAX_COORDINATE || y < MIN_COORDINATE || y > MAX_COORDINATE) return;
synchronized((UnicodeRasterFont.this).monitor)
{
label0:
{
int r;
int ax;
int ay;
int left = (r = rectangle) >> 24;
int bottom = (byte) (r >> 16);
int width = r >> 8 & 0xff;
int height = r & 0xff;
int right = left + width - 1;
int top = bottom + height - 1;
int scan;
if(!opaque)
{
if(x >= left && x <= right && y >= bottom && y <= top)
{
ax = x - left;
ay = y - bottom;
scan = bytesNeeded(width);
pixels[ay * scan + (ax >> 3)] &= ~(1 << (ax & 7));
if(x == left || x == right || y == bottom || y == top) trim();
}
break label0;
}
if((r & 0xffff) == 0)
{
madeLarger(rectangle = x << 24 | (y & 0xff) << 16 | 0x0101)[0] = 1;
break label0;
}
if(x >= left && x <= right && y >= bottom && y <= top)
{
ax = x - left;
ay = y - bottom;
scan = bytesNeeded(width);
pixels[ay * scan + (ax >> 3)] |= (1 << (ax & 7));
break label0;
}
ax = x - ((r = expand(x < left ? x - left : x > right ? x - right : 0, y < bottom ? y - bottom : y > top ? y - top : 0)) >> 24);
ay = y - ((byte) (r >> 16));
scan = bytesNeeded(r >> 8 & 0xff);
pixels[ay * scan + (ax >> 3)] |= (1 << (ax & 7));
}
}
}
public void setCharacterCode(int characterCode) {
int error = 0;
UnicodeRasterFont parent;
synchronized((parent = UnicodeRasterFont.this).monitor)
{
label0:
{
int count;
int newIndex;
int oldIndex;
int oldCharacterCode;
Glyph[] glyphs;
if(characterCode == (oldCharacterCode = this.characterCode)) break label0;
if(removed)
{
this.characterCode = characterCode;
break label0;
}
if(indexOf(characterCode, glyphs = parent.glyphs, count = parent.getGlyphsCount()) >= 0)
{
error = 1;
break label0;
}
newIndex = count;
oldIndex = indexOf(oldCharacterCode, glyphs, count);
for(int i = 0; i < count; i++) if(glyphs[i].characterCode > characterCode)
{
newIndex = i;
break;
}
if(newIndex < oldIndex)
{
Array.copy(glyphs, newIndex, glyphs, newIndex + 1, oldIndex - newIndex);
}
else if(newIndex > oldIndex && --newIndex > oldIndex)
{
Array.copy(glyphs, oldIndex + 1, glyphs, oldIndex, newIndex - oldIndex);
}
glyphs[newIndex] = this;
this.characterCode = characterCode;
}
}
if(error == 1)
{
throw new CharacterCodeExistsException(
(new StringBuilder()).append("Glyph.setCharacterCode: глиф с кодом ").append(characterCode).append(" уже существует в родительском шрифте.").toString()
);
}
}
public void setWidth(int width) {
if(width < MIN_UNSIGNED_VALUE) width = MIN_UNSIGNED_VALUE;
if(width > MAX_UNSIGNED_VALUE) width = MAX_UNSIGNED_VALUE;
synchronized((UnicodeRasterFont.this).monitor)
{
this.width = width;
}
}
public void getPixels(int[] pixels, int offset, int scanlength, int pixel) {
int error;
if(pixels == null)
{
throw new NullPointerException("Glyph.getPixels: аргумент pixels равен нулевой ссылке.");
}
error = 0;
synchronized((UnicodeRasterFont.this).monitor)
{
label0:
{
int r;
int width = (r = rectangle) >> 8 & 0xff;
int height = r & 0xff;
int pixelsOffset;
int pixelsLength;
byte[] p;
if(scanlength > -width && scanlength < width)
{
error = 1;
break label0;
}
if(scanlength >= 0)
{
pixelsOffset = offset;
pixelsLength = width + (height - 1) * scanlength;
} else
{
pixelsOffset = offset + (height - 1) * scanlength;
pixelsLength = width + offset - pixelsOffset;
}
if(!Array.isBoundValid(pixels.length, pixelsOffset, pixelsLength))
{
error = 2;
break label0;
}
p = this.pixels;
for(int scan = bytesNeeded(width), step = -scanlength - width, offs = offset + scanlength * (height - 1), y = 0; y < height; offs += step, y++)
{
for(int x = 0; x < width; offs++, x++) if((p[y * scan + (x >> 3)] & (1 << (x & 7))) != 0) pixels[offs] = pixel;
}
}
}
switch(error)
{
case 1:
throw new IllegalArgumentException("Glyph.getPixels: аргумент scanlength выходит из диапазона.");
case 2:
throw new ArrayIndexOutOfBoundsException("Glyph.getPixels: индекс массива выходит из диапазона.");
}
}
public boolean getPixel(int x, int y) {
boolean result;
synchronized((UnicodeRasterFont.this).monitor)
{
int r;
int left = (r = rectangle) >> 24;
int bottom = (byte) (r >> 16);
int width = r >> 8 & 0xff;
int right = left + width;
int top = bottom + (r & 0xff);
if(x < left || x >= right || y < bottom || y >= top)
{
result = false;
} else
{
int scan;
x -= left;
y -= bottom;
scan = bytesNeeded(width);
result = (pixels[y * scan + (x >> 3)] & (1 << (x & 7))) != 0;
}
}
return result;
}
public int getCharacterCode() {
return characterCode;
}
public int getWidth() {
return width;
}
public int getRawLeft() {
return rectangle >> 24;
}
public int getRawBottom() {
return (byte) (rectangle >> 16);
}
public int getRawWidth() {
return rectangle >> 8 & 0xff;
}
public int getRawHeight() {
return rectangle & 0xff;
}
public Glyph copy() {
int c;
int w;
int r;
byte[] p;
UnicodeRasterFont parent;
synchronized((parent = UnicodeRasterFont.this).monitor)
{
int len = bytesNeeded((r = rectangle) >> 8 & 0xff, r & 0xff);
c = characterCode;
w = width;
Array.copy(pixels, 0, p = new byte[len < 15 ? 15 : len + 1], 0, len);
}
return parent.new Glyph(true, c, w, r, p);
}
public UnicodeRasterFont parentFont() {
return removed ? null : UnicodeRasterFont.this;
}
final void trim() {
int r;
int count;
int left = (r = rectangle) >> 24;
int bottom = (byte) (r >> 16);
int width = r >> 8 & 0xff;
int height = r & 0xff;
int scan = bytesNeeded(width);
byte[] pixels = this.pixels;
/* глиф пуст? */
if(isEmptyGlyph(width, height, pixels))
{
rectangle = 0;
return;
}
/* пустые строки пикселов на вершине глифа? */
count = 0;
for(int row = height; row-- > 0; count++) if(!isEmptyRow(width, height, pixels, row)) break;
height -= count;
/* пустые строки пикселов на дне глифа? */
count = 0;
for(int row = 0; row < height; count++, row++) if(!isEmptyRow(width, height, pixels, row)) break;
if(count > 0)
{
bottom += count;
height -= count;
Array.copy(pixels, count * scan, pixels, 0, height * scan);
}
/* пустые столбцы пикселов на правой стороне глифа? */
count = 0;
for(int col = width; col-- > 0; count++) if(!isEmptyCol(width, height, pixels, col)) break;
if(count > 0)
{
int newWidth;
int newScan = bytesNeeded(newWidth = width - count);
if(scan > newScan) for(int index = scan, newIndex = newScan, row = height; row-- > 1; index += scan, newIndex += newScan) Array.copy(pixels, index, pixels, newIndex, newScan);
width = newWidth;
scan = newScan;
}
/* пустые столбцы пикселов на левой стороне глифа? */
count = 0;
for(int col = 0; col < width; count++, col++) if(!isEmptyCol(width, height, pixels, col)) break;
if(count > 0)
{
int newWidth;
int newScan = bytesNeeded(newWidth = width - count);
if((count & 7) > 0)
{
int ofs0 = count >> 3;
int ofs1 = 1 + ofs0;
int bits1 = count & 7;
int bits0 = 8 - bits1;
int mask1 = (1 << bits1) - 1;
int mask0 = 0xff - mask1;
for(int mend = maskEnding(newWidth), c = newScan - 1, f0 = ofs0, f1 = ofs1, to = 0, row = height; row-- > 0; f0 += scan, f1 += scan, to += scan) for(int i = 0; i <= c; i++)
{
int mask = i < c ? 0xff : mend;
pixels[to + i] = (byte) (((pixels[f0 + i] & mask0) >> bits1 | (pixels[f1 + i] & mask1) << bits0) & mask);
}
} else
{
int delta = count >> 3;
Array.copy(pixels, delta, pixels, 0, scan * height - delta);
}
if(scan > newScan) for(int index = scan, newIndex = newScan, row = height; row-- > 1; index += scan, newIndex += newScan) Array.copy(pixels, index, pixels, newIndex, newScan);
left += count;
width = newWidth;
}
/* обновление прямоугольника глифа */
rectangle = madeRectangle(left, bottom, width, height);
}
final int expand(int expandX, int expandY) {
int r;
int count;
int left = (r = rectangle) >> 24;
int bottom = (byte) (r >> 16);
int width = r >> 8 & 0xff;
int height = r & 0xff;
int scan = bytesNeeded(width);
byte[] pixels = madeLarger(width + (expandX >= 0 ? expandX : -expandX), height + (expandY >= 0 ? expandY : -expandY));
/* добавление пустых столбцов пикселов на левую сторону глифа */
if(expandX < 0)
{
int newWidth;
int newScan = bytesNeeded(newWidth = width + (count = -expandX));
if(scan < newScan) for(int index = scan * (height - 1), newIndex = newScan * (height - 1), row = height; row-- > 1; index -= scan, newIndex -= newScan)
{
Array.copy(pixels, index, pixels, newIndex, scan);
}
if((count & 7) > 0)
{
int len = count >> 3;
int bits0 = count & 7;
int bits1 = 8 - bits0;
int mask1 = (1 << bits1) - 1;
int mask0 = 0xff - mask1;
for(int mend = maskEnding(newWidth), c = newScan - len - 2, f0 = 0, f1 = 1, to = len + 1, row = height; row-- > 0; f0 += newScan, f1 += newScan, to += newScan)
{
int mask;
for(int i = c; i >= 0; i--)
{
mask = i < c ? 0xff : mend;
pixels[to + i] = (byte) (((pixels[f0 + i] & mask0) >> bits1 | (pixels[f1 + i] & mask1) << bits0) & mask);
}
mask = -1 < c ? 0xff : mend;
pixels[to - 1] = (byte) (((pixels[f0] & mask1) << bits0) & mask);
Array.fill(pixels, f0, len, 0);
}
} else
{
int delta = count >> 3;
Array.copy(pixels, 0, pixels, delta, newScan * height - delta);
for(int index = 0, row = height; row-- > 0; index += newScan) Array.fill(pixels, index, delta, 0);
}
left += expandX;
width = newWidth;
scan = newScan;
}
/* добавление пустых столбцов пикселов на правую сторону глифа */
if(expandX > 0)
{
int newWidth;
int newScan = bytesNeeded(newWidth = width + expandX);
if(scan < newScan) for(int index = scan * (height - 1), newIndex = newScan * (height - 1), row = height; row-- > 1; index -= scan, newIndex -= newScan)
{
Array.copy(pixels, index, pixels, newIndex, scan);
}
for(int mend = maskEnding(width), c = scan - 1, index = scan, len = newScan - scan, row = height; row-- > 0; c += newScan, index += newScan)
{
pixels[c] &= mend;
Array.fill(pixels, index, len, 0);
}
width = newWidth;
scan = newScan;
}
/* добавление пустых строк пикселов на дно глифа */
if(expandY < 0)
{
Array.copy(pixels, 0, pixels, count = -expandY * scan, height * scan);
Array.fill(pixels, 0, count, 0);
bottom += expandY;
height -= expandY;
}
/* добавление пустых строк пикселов на вершину глифа */
if(expandY > 0)
{
Array.fill(pixels, height * scan, expandY * scan, 0);
height += expandY;
}
/* обновление прямоугольника глифа */
return rectangle = madeRectangle(left, bottom, width, height);
}
final int getDataSize() {
int r;
return bytesNeeded((r = rectangle) >> 8 & 0xff, r & 0xff) + 5;
}
final byte[] madeLarger(int rectangle) {
int len;
int length = bytesNeeded(rectangle >> 8 & 0xff, rectangle & 0xff) + 1;
byte[] result = pixels;
if(length > (len = result.length))
{
int newLength = (len << 1) + 1;
int resLength = length >= newLength ? length : newLength;
Array.copy(result, 0, result = pixels = new byte[resLength], 0, len);
}
return result;
}
final byte[] madeLarger(int width, int height) {
int len;
int length = bytesNeeded(width, height) + 1;
byte[] result = pixels;
if(length > (len = result.length))
{
int newLength = (len << 1) + 1;
int resLength = length >= newLength ? length : newLength;
Array.copy(result, 0, result = pixels = new byte[resLength], 0, len);
}
return result;
}
final Object monitor() {
return (UnicodeRasterFont.this).monitor;
}
}
private class Enumerator extends Object implements Enumeration
{
private int index;
public Enumerator() {
}
public boolean hasMoreElements() {
return index < (UnicodeRasterFont.this).count;
}
public Object nextElement() {
int error = 0;
Glyph result;
UnicodeRasterFont parent;
synchronized((parent = UnicodeRasterFont.this).monitor)
{
label0:
{
int i;
if((i = index) >= parent.count)
{
error = 1;
result = null;
break label0;
}
result = parent.glyphs[i++];
index = i;
}
}
if(error == 1)
{
throw new NoSuchElementException("Enumeration.nextElement: элементов больше не осталось.");
}
return result;
}
}
int count;
long attributes;
Glyph[] glyphs;
String lastFileName;
final Object monitor;
public UnicodeRasterFont() {
this.glyphs = new Glyph[15];
this.monitor = new Object();
}
public void loadFromInputStream(InputStream stream) throws IOException {
loadFromDataStream(new ExtendedDataInputStream(stream));
}
public void loadFromDataStream(ExtendedDataInputStream stream) throws IOException {
int error;
if(!stream.markSupported())
{
throw new InvalidStreamException("UnicodeRasterFont.loadFromDataStream: поток ввода данных не поддерживает методы mark и reset.");
}
stream.mark(Integer.MAX_VALUE);
error = 0;
synchronized(monitor)
{
label0:
{
int len = count;
Glyph[] g = glyphs;
InputStream src;
for(int i = len; i-- > 0; g[i].removed = true);
Array.fill(g, count = 0, len, null);
attributes = stream.readLong();
if((len = stream.readIntLE()) < 0)
{
error = 1;
break label0;
}
if(g.length < (count = len)) glyphs = g = new Glyph[len + 15];
for(int i = 0; i < len; i++)
{
int characterCode;
long position;
stream.reset();
stream.skip(((long) i << 3) + 12L);
characterCode = stream.readIntLE();
position = (long) stream.readIntLE() & 0x00000000ffffffffL;
stream.reset();
stream.skip(position - 4L);
(g[i] = this.new Glyph(false, characterCode, 0, 0, new byte[15])).loadFromDataStream(stream);
}
if((src = stream.getSourceStream()) instanceof HandleInputStream) lastFileName = ((HandleInputStream) src).getFileName();
}
}
if(error == 1)
{
throw new InvalidDataFormatException("UnicodeRasterFont.loadFromDataStream: неправильный формат файла шрифта UFN.");
}
}
public void saveToOutputStream(OutputStream stream) throws IOException {
saveToDataStream(new ExtendedDataOutputStream(stream));
}
public void saveToDataStream(ExtendedDataOutputStream stream) throws IOException {
synchronized(monitor)
{
int len = count;
int offset;
int startoffset = offset = 16 + (len << 3);
Glyph[] g = glyphs;
OutputStream dst;
label0: for(int i = 0; i < len; i++)
{
int w;
int r;
int rw;
int rh;
byte[] p;
Glyph curr;
w = (curr = g[i]).getWidth();
rw = (r = curr.rectangle) >> 8 & 0xff;
rh = r & 0xff;
p = curr.pixels;
for(int j = 0; j < i; j++)
{
Glyph jgl = g[j];
if(w == jgl.getWidth() && r == jgl.rectangle && pixelsMatches(rw, rh, p, jgl.pixels))
{
curr.offset = jgl.offset;
continue label0;
}
}
curr.offset = offset;
offset += curr.getDataSize();
}
offset = startoffset;
stream.writeInt((int) SIGNATURE);
stream.writeLong(attributes);
stream.writeIntLE(len);
for(int i = 0; i < len; i++)
{
Glyph curr = g[i];
stream.writeIntLE(curr.getCharacterCode());
stream.writeIntLE(curr.offset);
}
for(int i = 0; i < len; i++)
{
Glyph curr = g[i];
if(offset == curr.offset)
{
curr.saveToDataStream(stream);
offset += curr.getDataSize();
}
}
if((dst = stream.getDestinationStream()) instanceof HandleOutputStream) lastFileName = ((HandleOutputStream) dst).getFileName();
}
}
public void clear() {
synchronized(monitor)
{
int len = count;
Glyph[] g = glyphs;
for(int i = len; i-- > 0; g[i].removed = true);
Array.fill(g, count = 0, len, null);
attributes = 0L;
}
}
public boolean isEmpty() {
boolean result;
synchronized(monitor)
{
result = ((long) count | attributes) == 0L;
}
return result;
}
public void removeGlyph(int characterCode) {
synchronized(monitor)
{
int len;
int index;
Glyph[] g;
if((index = indexOf(characterCode, g = glyphs, len = count)) >= 0)
{
g[index].removed = true;
if(index < --len) Array.copy(g, index + 1, g, index, len - index);
g[count = len] = null;
}
}
}
public void removeGlyph(Glyph character) {
synchronized(monitor)
{
int len;
int index;
Glyph[] g;
if((index = indexOf(character, g = glyphs, len = count)) >= 0)
{
g[index].removed = true;
if(index < --len) Array.copy(g, index + 1, g, index, len - index);
g[count = len] = null;
}
}
}
public void setBold(boolean bold) {
synchronized(monitor)
{
attributes = bold ? attributes | (1L << 56) : attributes & ~(1L << 56);
}
}
public void setItalic(boolean italic) {
synchronized(monitor)
{
attributes = italic ? attributes | (1L << 57) : attributes & ~(1L << 57);
}
}
public void setHeight(int height) {
if(height < MIN_UNSIGNED_VALUE) height = MIN_UNSIGNED_VALUE;
if(height > MAX_UNSIGNED_VALUE) height = MAX_UNSIGNED_VALUE;
synchronized(monitor)
{
attributes = attributes & ~(0xffL << 48) | (long) height << 48;
}
}
public void setBaselineHeight(int baselineHeight) {
if(baselineHeight < MIN_UNSIGNED_VALUE) baselineHeight = MIN_UNSIGNED_VALUE;
if(baselineHeight > MAX_UNSIGNED_VALUE) baselineHeight = MAX_UNSIGNED_VALUE;
synchronized(monitor)
{
attributes = attributes & ~(0xffL << 40) | (long) baselineHeight << 40;
}
}
public void setSmallLettersHeight(int smallLettersHeight) {
if(smallLettersHeight < MIN_UNSIGNED_VALUE) smallLettersHeight = MIN_UNSIGNED_VALUE;
if(smallLettersHeight > MAX_UNSIGNED_VALUE) smallLettersHeight = MAX_UNSIGNED_VALUE;
synchronized(monitor)
{
attributes = attributes & ~(0xffL << 32) | (long) smallLettersHeight << 32;
}
}
public void setCapitalLettersHeight(int capitalLettersHeight) {
if(capitalLettersHeight < MIN_UNSIGNED_VALUE) capitalLettersHeight = MIN_UNSIGNED_VALUE;
if(capitalLettersHeight > MAX_UNSIGNED_VALUE) capitalLettersHeight = MAX_UNSIGNED_VALUE;
synchronized(monitor)
{
attributes = attributes & ~(0xffL << 24) | (long) capitalLettersHeight << 24;
}
}
public void setStrikeoutLinePosition(int strikeoutLinePosition) {
if(strikeoutLinePosition < MIN_SIGNED_VALUE) strikeoutLinePosition = MIN_SIGNED_VALUE;
if(strikeoutLinePosition > MAX_SIGNED_VALUE) strikeoutLinePosition = MAX_SIGNED_VALUE;
synchronized(monitor)
{
attributes = attributes & ~(0xffL << 8) | (long) (strikeoutLinePosition & 0xff) << 8;
}
}
public void setUnderscoreLinePosition(int underscoreLinePosition) {
if(underscoreLinePosition < MIN_SIGNED_VALUE) underscoreLinePosition = MIN_SIGNED_VALUE;
if(underscoreLinePosition > MAX_SIGNED_VALUE) underscoreLinePosition = MAX_SIGNED_VALUE;
synchronized(monitor)
{
attributes = attributes & ~(0xffL << 16) | (long) (underscoreLinePosition & 0xff) << 16;
}
}
public void setLinesWidth(int linesWidth) {
if(linesWidth < MIN_UNSIGNED_VALUE) linesWidth = MIN_UNSIGNED_VALUE;
if(linesWidth > MAX_UNSIGNED_VALUE) linesWidth = MAX_UNSIGNED_VALUE;
synchronized(monitor)
{
attributes = attributes & ~0xffL | (long) linesWidth;
}
}
public boolean isBold() {
return (attributes & (1L << 56)) != 0L;
}
public boolean isItalic() {
return (attributes & (1L << 57)) != 0L;
}
public int getHeight() {
return (int) (attributes >> 48) & 0xff;
}
public int getBaselineHeight() {
return (int) (attributes >> 40) & 0xff;
}
public int getSmallLettersHeight() {
return (int) (attributes >> 32) & 0xff;
}
public int getCapitalLettersHeight() {
return (int) (attributes >> 24) & 0xff;
}
public int getStrikeoutLinePosition() {
return (byte) (attributes >> 8);
}
public int getUnderscoreLinePosition() {
return (byte) (attributes >> 16);
}
public int getLinesWidth() {
int result;
return (result = (int) attributes & 0xff) == 0 ? 1 : result;
}
public int getGlyphsCount() {
return count;
}
public String getLastFileName() {
return lastFileName;
}
public Glyph getGlyph(int characterCode) {
Glyph result;
synchronized(monitor)
{
int index;
Glyph[] g;
result = (index = indexOf(characterCode, g = glyphs, count)) >= 0 ? g[index] : null;
}
return result;
}
public Glyph addGlyph(int characterCode) {
int error = 0;
Glyph result;
synchronized(monitor)
{
label0:
{
int len;
Glyph[] g;
if(indexOf(characterCode, g = glyphs, len = count) >= 0)
{
error = 1;
result = null;
break label0;
}
if(len == g.length) Array.copy(g, 0, g = glyphs = new Glyph[(len << 1) + 1], 0, len);
result = this.new Glyph(false, characterCode, 0, 0, new byte[15]);
for(int i = 0; i < len; i++) if(g[i].getCharacterCode() > characterCode)
{
Array.copy(g, i, g, i + 1, len++ - i);
g[i] = result;
count = len;
break label0;
}
g[len++] = result;
count = len;
}
}
if(error == 1)
{
throw new CharacterCodeExistsException((new StringBuilder()).append("UnicodeRasterFont.addGlyph: глиф с кодом ").append(characterCode).append(" уже существует в этом шрифте.").toString());
}
return result;
}
public Enumeration glyphs() {
return this.new Enumerator();
}
}