mirror of
https://github.com/coderkei/akmenu-next.git
synced 2025-06-19 09:25:33 -04:00
291 lines
11 KiB
C++
291 lines
11 KiB
C++
/*
|
|
font_pcf.cpp
|
|
Copyright (C) 2008-2009 somebody
|
|
Copyright (C) 2009 yellow wood goblin
|
|
|
|
SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
#include "font_pcf.h"
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include "font_pcf_internals.h"
|
|
#include "language.h"
|
|
|
|
cFontPcf::cFontPcf()
|
|
: cFont(),
|
|
iGlyphs(NULL),
|
|
iData(NULL),
|
|
iCount(0),
|
|
iDataSize(0),
|
|
iHeight(0),
|
|
iAscent(0),
|
|
iDescent(0) {
|
|
// FIXME: test on nds
|
|
// printf("%d\n",sizeof(cFontPcf::SGlyph));
|
|
}
|
|
|
|
cFontPcf::~cFontPcf() {
|
|
delete[] iGlyphs;
|
|
delete[] iData;
|
|
}
|
|
|
|
void cFontPcf::Info(const char* aString, u32* aWidth, u32* aSymbolCount) {
|
|
u32 len;
|
|
u32 code = utf8toucs2((const u8*)aString, &len);
|
|
if (aSymbolCount) *aSymbolCount = len;
|
|
if (aWidth) {
|
|
s32 index = Search(code);
|
|
*aWidth = (index >= 0) ? iGlyphs[index].iWidth : ((code > 0 && code < 8) ? 10 : 0);
|
|
}
|
|
}
|
|
|
|
bool cFontPcf::ParseAccels(int aFont, u32 aSize, u32 aOffset) {
|
|
bool res = false;
|
|
if (lseek(aFont, aOffset, SEEK_SET) < 0) return false;
|
|
SPcfAccel header;
|
|
if (read(aFont, &header, sizeof(header)) != sizeof(header)) return false;
|
|
res = true;
|
|
iAscent = header.iFontAscent;
|
|
iDescent = header.iFontDescent;
|
|
iHeight = iAscent + iDescent;
|
|
return res;
|
|
}
|
|
|
|
bool cFontPcf::ParseBitmaps(int aFont, u32 aSize, u32 aOffset) {
|
|
bool res = false;
|
|
if (lseek(aFont, aOffset, SEEK_SET) < 0) return false;
|
|
SPcfBitmapsHeader header;
|
|
if (read(aFont, &header, sizeof(header)) != sizeof(header)) return false;
|
|
iGlyphs = new (std::nothrow) SGlyph[header.iCount];
|
|
if (!iGlyphs) return false;
|
|
iCount = header.iCount;
|
|
iDataSize = aSize - sizeof(SPcfBitmapsHeader) - header.iCount * sizeof(u32) - 16;
|
|
iData = new (std::nothrow) u8[iDataSize];
|
|
if (iData) {
|
|
u32* offsets = new (std::nothrow) u32[header.iCount];
|
|
if (offsets) {
|
|
do {
|
|
if (read(aFont, offsets, sizeof(u32) * header.iCount) !=
|
|
(ssize_t)(sizeof(u32) * header.iCount))
|
|
break;
|
|
for (u32 ii = 0; ii < header.iCount; ii++) {
|
|
iGlyphs[ii].iOffset = offsets[ii];
|
|
}
|
|
if (lseek(aFont, 16, SEEK_CUR) < 0) break;
|
|
if (read(aFont, iData, iDataSize) != (ssize_t)iDataSize) break;
|
|
res = true;
|
|
} while (false);
|
|
delete[] offsets;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool cFontPcf::ParseMetrics(int aFont, u32 aSize, u32 aOffset) {
|
|
bool res = false;
|
|
if (lseek(aFont, aOffset, SEEK_SET) < 0) return false;
|
|
u8* buffer = new (std::nothrow) u8[aSize];
|
|
if (buffer) {
|
|
if (read(aFont, buffer, aSize) == (ssize_t)aSize) {
|
|
u32 format = *(u32*)buffer;
|
|
if (format & PCF_COMPRESSED_METRICS) {
|
|
u32 count = *(u16*)(buffer + 4);
|
|
if (count == iCount) {
|
|
SPcfCompressedMetric* metrics = (SPcfCompressedMetric*)(buffer + 6);
|
|
for (u32 ii = 0; ii < iCount; ii++) {
|
|
iGlyphs[ii].iWidth = metrics[ii].iCharacterWidth - 0x80;
|
|
iGlyphs[ii].iLeft = metrics[ii].iLeftSideBearing - 0x80;
|
|
iGlyphs[ii].iRight = metrics[ii].iRightSideBearing - 0x80;
|
|
iGlyphs[ii].iAscent = metrics[ii].iAscent - 0x80;
|
|
iGlyphs[ii].iDescent = metrics[ii].iDescent - 0x80;
|
|
// printf("0x%08x:
|
|
// w=%d,l=%d,r=%d,a=%d,d=%d,offset=0x%08x\n",iGlyphs[ii].iCode,iGlyphs[ii].iWidth,iGlyphs[ii].iLeft,iGlyphs[ii].iRight,iGlyphs[ii].iAscent,iGlyphs[ii].iDescent,iGlyphs[ii].iOffset);
|
|
}
|
|
res = true;
|
|
}
|
|
}
|
|
}
|
|
delete[] buffer;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool cFontPcf::ParseEncodings(int aFont, u32 aSize, u32 aOffset) {
|
|
bool res = false;
|
|
if (lseek(aFont, aOffset, SEEK_SET) < 0) return false;
|
|
u8* buffer = new (std::nothrow) u8[aSize];
|
|
if (buffer) {
|
|
if (read(aFont, buffer, aSize) == (ssize_t)aSize) {
|
|
SPcfEncodingsHeader& header = *(SPcfEncodingsHeader*)(buffer);
|
|
u32 nencoding = (header.iLastCol - header.iFirstCol + 1) *
|
|
(header.iLastRow - header.iFirstRow + 1);
|
|
u16* codes = (u16*)(buffer + sizeof(SPcfEncodingsHeader));
|
|
for (u32 ii = 0; ii < nencoding; ii++) {
|
|
if (codes[ii] != 0xffff) {
|
|
iGlyphs[codes[ii]].iCode =
|
|
(((ii / (header.iLastCol - header.iFirstCol + 1)) + header.iFirstRow) *
|
|
256) +
|
|
((ii % (header.iLastCol - header.iFirstCol + 1)) + header.iFirstCol);
|
|
}
|
|
}
|
|
res = true;
|
|
}
|
|
delete[] buffer;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int cFontPcf::Compare(const void* a, const void* b) {
|
|
return (static_cast<const SGlyph*>(a)->iCode - static_cast<const SGlyph*>(b)->iCode);
|
|
}
|
|
|
|
bool cFontPcf::Load(const char* aFileName) {
|
|
bool res = false;
|
|
int font = open(aFileName, O_RDONLY);
|
|
if (font >= 0) {
|
|
do {
|
|
SPcfHeader header;
|
|
if (read(font, &header, sizeof(header)) != sizeof(header)) break;
|
|
if (header.iVersion != PCF_FILE_VERSION) break;
|
|
SPcfEntry entries[header.iCount];
|
|
if (read(font, entries, sizeof(SPcfEntry) * header.iCount) !=
|
|
(ssize_t)(sizeof(SPcfEntry) * header.iCount))
|
|
break;
|
|
s32 accelsIndex = -1, bitmapsIndex = -1, metricsIndex = -1, encodingsIndex = -1;
|
|
for (u32 ii = 0; ii < header.iCount; ii++) {
|
|
if (entries[ii].iType == PCF_ACCELERATORS)
|
|
accelsIndex = ii;
|
|
else if (entries[ii].iType == PCF_BITMAPS)
|
|
bitmapsIndex = ii;
|
|
else if (entries[ii].iType == PCF_METRICS)
|
|
metricsIndex = ii;
|
|
else if (entries[ii].iType == PCF_BDF_ENCODINGS)
|
|
encodingsIndex = ii;
|
|
}
|
|
if (accelsIndex >= 0 && bitmapsIndex >= 0 && metricsIndex >= 0 && encodingsIndex >= 0) {
|
|
if (!ParseAccels(font, entries[accelsIndex].iSize, entries[accelsIndex].iOffset))
|
|
break;
|
|
if (!ParseBitmaps(font, entries[bitmapsIndex].iSize, entries[bitmapsIndex].iOffset))
|
|
break;
|
|
if (!ParseMetrics(font, entries[metricsIndex].iSize, entries[metricsIndex].iOffset))
|
|
break;
|
|
if (!ParseEncodings(font, entries[encodingsIndex].iSize,
|
|
entries[encodingsIndex].iOffset))
|
|
break;
|
|
if (lang().GetInt("font", "sort", 0))
|
|
qsort(iGlyphs, iCount, sizeof(SGlyph), Compare);
|
|
res = true;
|
|
}
|
|
} while (false);
|
|
close(font);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
s32 cFontPcf::Search(u16 aCode) {
|
|
s32 result = SearchInternal(aCode);
|
|
if (result < 0 && aCode > ' ') result = SearchInternal('?');
|
|
return result;
|
|
}
|
|
|
|
s32 cFontPcf::SearchInternal(u16 aCode) {
|
|
s32 low = 0, high = iCount - 1, curr;
|
|
while (true) {
|
|
curr = (low + high) / 2;
|
|
// curr=((aCode-iGlyphs[low].iCode)*(high-low)/(iGlyphs[high].iCode-iGlyphs[low].iCode))+low;
|
|
if (aCode < iGlyphs[curr].iCode) {
|
|
high = curr - 1;
|
|
} else if (aCode > iGlyphs[curr].iCode) {
|
|
low = curr + 1;
|
|
} else {
|
|
return curr;
|
|
}
|
|
if (low > high) return -1;
|
|
}
|
|
}
|
|
|
|
void cFontPcf::DrawInternal(u16* mem, s16 x, s16 y, const u8* data, u16 color, u32 width,
|
|
u32 height) {
|
|
u32 byteW = width;
|
|
byteW = byteW / 8 + (byteW & 7 ? 1 : 0);
|
|
for (u32 ii = 0; ii < height; ii++) {
|
|
u32 cur_width = width;
|
|
for (u32 jj = 0; jj < byteW; jj++) {
|
|
u32 curr = *data++;
|
|
u32 top = (cur_width > 8) ? 8 : cur_width;
|
|
for (u32 kk = 0; kk < top; kk++) {
|
|
if (curr & (1 << kk)) {
|
|
if (y + ii >= 0 && x + jj >= 0) {
|
|
*(mem + x + (jj << 3) + kk + ((y + ii) << 8)) = color;
|
|
}
|
|
}
|
|
}
|
|
cur_width -= 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cFontPcf::Draw(u16* mem, s16 x, s16 y, const u8* aText, u16 color) {
|
|
if (!(iData && iGlyphs)) return;
|
|
u32 code = utf8toucs2(aText, NULL);
|
|
s32 index = Search(code);
|
|
if (index >= 0) {
|
|
x += iGlyphs[index].iLeft;
|
|
y += -iGlyphs[index].iAscent + iAscent;
|
|
DrawInternal(mem, x, y, iData + iGlyphs[index].iOffset, color,
|
|
-iGlyphs[index].iLeft + iGlyphs[index].iRight,
|
|
iGlyphs[index].iAscent + iGlyphs[index].iDescent);
|
|
} else if (code > 0 && code < 8) {
|
|
y += -9 + iAscent;
|
|
u8 special[] = {
|
|
0x7c, 0, 0xfe, 0, 0xef, 1, 0xd7, 1, 0xd7, 1, 0x83, 1, 0xbb, 1, 0xfe, 0, 0x7c, 0,
|
|
0x7c, 0, 0xfe, 0, 0xc3, 1, 0xbb, 1, 0xc3, 1, 0xbb, 1, 0xc3, 1, 0xfe, 0, 0x7c, 0,
|
|
0x7c, 0, 0xfe, 0, 0xbb, 1, 0xd7, 1, 0xef, 1, 0xd7, 1, 0xbb, 1, 0xfe, 0, 0x7c, 0,
|
|
0x7c, 0, 0xfe, 0, 0xbb, 1, 0xd7, 1, 0xef, 1, 0xef, 1, 0xef, 1, 0xfe, 0, 0x7c, 0,
|
|
0xf8, 1, 0xfc, 1, 0xf6, 1, 0xf7, 1, 0xf7, 1, 0xf7, 1, 0x87, 1, 0xff, 1, 0xff, 1,
|
|
0x3f, 0, 0x7f, 0, 0xc3, 0, 0xbb, 1, 0xc3, 1, 0xdb, 1, 0xbb, 1, 0xff, 1, 0xff, 1,
|
|
0x38, 0, 0x38, 0, 0x38, 0, 0xff, 1, 0xef, 1, 0xff, 1, 0x38, 0, 0x38, 0, 0x38, 0,
|
|
};
|
|
DrawInternal(mem, x, y, special + (code - 1) * 18, color, 9, 9);
|
|
}
|
|
}
|
|
|
|
#define ONE_BYTE_MASK 0x80
|
|
#define ONE_BYTE_SIGN 0x00
|
|
#define TWO_BYTE_MASK 0xc0e0
|
|
#define TWO_BYTE_SIGN 0x80c0
|
|
#define THREE_BYTE_MASK 0xc0c0f0
|
|
#define THREE_BYTE_SIGN 0x8080e0
|
|
#define FIRST_BYTE 0xff
|
|
#define SECOND_BYTE 0xff00
|
|
#define THIRD_BYTE 0xff0000
|
|
|
|
u32 cFontPcf::utf8toucs2(const u8* aSource, u32* aLength) {
|
|
u32 data = aSource[0], len = 1, res = '?';
|
|
if ((data & ONE_BYTE_MASK) == ONE_BYTE_SIGN) {
|
|
res = data;
|
|
} else {
|
|
data += aSource[1] * 0x100;
|
|
if ((data & TWO_BYTE_MASK) == TWO_BYTE_SIGN) {
|
|
res = data & ~TWO_BYTE_MASK;
|
|
res = ((res & SECOND_BYTE) >> 8) | ((res & FIRST_BYTE) << 6);
|
|
len = 2;
|
|
} else {
|
|
data += aSource[2] * 0x10000;
|
|
if ((data & THREE_BYTE_MASK) == THREE_BYTE_SIGN) {
|
|
res = data & ~THREE_BYTE_MASK;
|
|
res = ((res & FIRST_BYTE) << 12) | ((res & SECOND_BYTE) >> 2) |
|
|
((res & THIRD_BYTE) >> 16);
|
|
len = 3;
|
|
}
|
|
}
|
|
}
|
|
if (aLength) *aLength = len;
|
|
return res;
|
|
}
|
|
|
|
u32 cFontPcf::FontRAM(void) {
|
|
return iDataSize + sizeof(SGlyph) * iCount;
|
|
}
|