Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UTF-8 support #27

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ endif()
set(LIBOPENUI_SRC
libopenui_file.cpp
bitmapbuffer.cpp
font.cpp
window.cpp
layer.cpp
form.cpp
Expand Down
108 changes: 83 additions & 25 deletions src/bitmapbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -899,17 +899,74 @@ void BitmapBuffer::drawMask(coord_t x, coord_t y, const BitmapMask * mask, const
}
}

uint8_t BitmapBuffer::drawChar(coord_t x, coord_t y, const Font::Glyph & glyph, LcdColor color)
#define INCREMENT_POS(delta) do { if (flags & VERTICAL) y -= delta; else x += delta; } while(0)

coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdColor color, LcdFlags flags)
{
if (glyph.width) {
drawMask(x, y, glyph.font->getBitmapData(), color, glyph.offset, 0, glyph.width);
MOVE_OFFSET();

auto font = getFont(flags);
int height = font->getHeight();

if (y + height <= ymin || y >= ymax) {
RESTORE_OFFSET();
return x;
}

if (flags & (RIGHT | CENTERED)) {
int width = font->getTextWidth(s, len);
if (flags & RIGHT) {
INCREMENT_POS(-width);
}
else if (flags & CENTERED) {
INCREMENT_POS(-width / 2);
}
}
return glyph.width;

coord_t & pos = (flags & VERTICAL) ? y : x;
const coord_t orig_pos = pos;

auto curr = s;
while (len == 0 || curr - s < len) {
auto c = getNextUnicodeChar(curr);

if (!c) {
break;
}

if (c == ' ') {
INCREMENT_POS(font->getSpaceWidth());
continue;
}

if (c == '\n') {
pos = orig_pos;
if (flags & VERTICAL)
x += height;
else
y += height;
continue;
}

auto glyph = font->getGlyph(c);
// TRACE("c = '%lc' 0x%X offset=%d width=%d", c, c, glyph.offset, glyph.width);
if (glyph.width) {
drawChar(x, y, glyph, color);
INCREMENT_POS(glyph.width + font->getSpacing());
}
else {
TRACE("Missing glyph '%lc' hex=0x%X", c, c);
INCREMENT_POS(font->getSpacing());
}
}

RESTORE_OFFSET();

return ((flags & RIGHT) ? orig_pos : pos) - offsetX;
}

#define INCREMENT_POS(delta) do { if (flags & VERTICAL) y -= delta; else x += delta; } while(0)

coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdColor color, LcdFlags flags)
coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const wchar_t * s, uint8_t len, LcdColor color, LcdFlags flags)
{
MOVE_OFFSET();

Expand All @@ -934,37 +991,38 @@ coord_t BitmapBuffer::drawSizedText(coord_t x, coord_t y, const char * s, uint8_
coord_t & pos = (flags & VERTICAL) ? y : x;
const coord_t orig_pos = pos;

for (int i = 0; len == 0 || i < len; ++i) {
unsigned int c = uint8_t(*s);
// TRACE("c = %d %o 0x%X '%c'", c, c, c, c);
auto curr = s;
while (len == 0 || curr - s < len) {
auto c = *curr++;

if (!c) {
break;
}
else if (c >= CJK_BYTE1_MIN) {
// CJK char
auto glyph = font->getCJKChar(c, *++s);
// TRACE("CJK = %d", c);
uint8_t width = drawChar(x, y, glyph, color);
INCREMENT_POS(width + CHAR_SPACING);
}
else if (c >= 0x20) {
auto glyph = font->getChar(c);
uint8_t width = drawChar(x, y, glyph, color);
if (c >= '0' && c <= '9')
INCREMENT_POS(font->getChar('9').width + CHAR_SPACING);
else
INCREMENT_POS(width + CHAR_SPACING);

if (c == ' ') {
INCREMENT_POS(font->getSpaceWidth());
continue;
}
else if (c == '\n') {

if (c == '\n') {
pos = orig_pos;
if (flags & VERTICAL)
x += height;
else
y += height;
continue;
}

s++;
auto glyph = font->getGlyph(c);
// TRACE("c = '%lc' 0x%X offset=%d width=%d", c, c, glyph.offset, glyph.width);
if (glyph.width) {
drawChar(x, y, glyph, color);
INCREMENT_POS(glyph.width + font->getSpacing());
}
else {
TRACE("Missing glyph '%lc' hex=0x%X", c, c);
INCREMENT_POS(font->getSpacing());
}
}

RESTORE_OFFSET();
Expand Down
11 changes: 10 additions & 1 deletion src/bitmapbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -647,12 +647,18 @@ class BitmapBuffer: public BitmapBufferBase<pixel_t>
void drawMask(coord_t x, coord_t y, const BitmapMask * mask, const BitmapBuffer * srcBitmap, coord_t offsetX = 0, coord_t offsetY = 0, coord_t width = 0, coord_t height = 0);

coord_t drawSizedText(coord_t x, coord_t y, const char * s, uint8_t len, LcdColor color, LcdFlags flags = 0);
coord_t drawSizedText(coord_t x, coord_t y, const wchar_t * s, uint8_t len, LcdColor color, LcdFlags flags = 0);

coord_t drawText(coord_t x, coord_t y, const char * s, LcdColor color, LcdFlags flags = 0)
{
return drawSizedText(x, y, s, 0, color, flags);
}

coord_t drawText(coord_t x, coord_t y, const wchar_t * s, LcdColor color, LcdFlags flags = 0)
{
return drawSizedText(x, y, s, 0, color, flags);
}

coord_t drawTextAtIndex(coord_t x, coord_t y, const char * s, uint8_t idx, LcdColor color, LcdFlags flags = 0)
{
char length = *s++;
Expand Down Expand Up @@ -716,7 +722,10 @@ class BitmapBuffer: public BitmapBufferBase<pixel_t>
return applyClippingRect(x, y, w, h);
}

uint8_t drawChar(coord_t x, coord_t y, const Font::Glyph & glyph, LcdColor color);
void drawChar(coord_t x, coord_t y, const Font::Glyph & glyph, LcdColor color)
{
drawMask(x, y, glyph.data, color, glyph.offset, 0, glyph.width);
}

inline void drawPixel(pixel_t * p, pixel_t value)
{
Expand Down
165 changes: 165 additions & 0 deletions src/font.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* Copyright (C) OpenTX
*
* Source:
* https://github.com/opentx/libopenui
*
* This file is a part of libopenui library.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*/

#include <ff.h>
#include <cinttypes>
#include "bitmapbuffer.h"
#include "font.h"
#include "debug.h"

BitmapData * decodeBitmapData(const uint8_t * data)
{
auto bitmap = (const BitmapData *)data;
size_t fontSize = bitmap->width() * bitmap->height();
auto * buffer = (uint8_t *)malloc(fontSize + 4);

// copy width / height
memcpy(buffer, data, 4);

RLEBitmap::decode(buffer + 4, fontSize, bitmap->getData());
return (BitmapData *)buffer;
}

void flipBitmapData(BitmapData * bitmap)
{
auto ptr1 = &bitmap->data[0];
auto ptr2 = &bitmap->data[bitmap->height() * bitmap->width() - 1];
while (ptr2 > ptr1) {
std::swap(*ptr1++, *ptr2--);
}
}

void rotateBitmapData(BitmapData * bitmap)
{
auto dataSize = bitmap->width() * bitmap->height();
auto * srcData = (uint8_t *)malloc(dataSize);
memcpy(srcData, bitmap->data, dataSize);
auto * destData = &bitmap->data[0];
for (coord_t y = 0; y < bitmap->height(); y++) {
for (coord_t x = 0; x < bitmap->width(); x++) {
destData[x * bitmap->height() + y] = srcData[y * bitmap->width() + x];
}
}
free(srcData);
}

/* Font format
* 'F', 'N', 'T', '\0'
* begin: 4bytes (glyphs index start)
* end: 4bytes (glyphs index end, not included)
* specs: 2bytes * (count + 1)
* data: bitmap data in RLE format
*/
bool Font::loadFile(const char * path)
{
TRACE("Font::loadFile('%s')", path);

auto file = (FIL *)malloc(sizeof(FIL));
if (!file) {
return false;
}

FRESULT result = f_open(file, path, FA_READ);
if (result != FR_OK) {
free(file);
return false;
}

struct {
char fmt[4];
char name[LEN_FONT_NAME + 1];
uint8_t rangesCount;
uint8_t spacing;
uint8_t spaceWidth;
} header;
UINT read;
result = f_read(file, (uint8_t *)&header, sizeof(header), &read);
if (result != FR_OK || read != sizeof(header) || strncmp(header.fmt, "FNT0", sizeof(header.fmt)) != 0) {
TRACE("loadFont('%s'): invalid header", path);
f_close(file);
free(file);
return false;
}

strlcpy(this->name, header.name, sizeof(this->name));
this->spacing = header.spacing;
this->spaceWidth = header.spaceWidth;

for (auto i = 0; i < header.rangesCount; i++) {
struct {
uint32_t begin;
uint32_t end;
uint32_t dataSize;
} rangeHeader;

result = f_read(file, (uint8_t *)&rangeHeader, sizeof(rangeHeader), &read);
if (result != FR_OK || read != sizeof(rangeHeader) || rangeHeader.begin >= rangeHeader.end) {
TRACE("loadFont('%s'): invalid range header", path);
f_close(file);
free(file);
return false;
}

uint32_t specsSize = sizeof(uint16_t) * (rangeHeader.end - rangeHeader.begin + 1);
auto specs = (uint16_t *)malloc(specsSize);
if (!specs) {
f_close(file);
free(file);
return false;
}

result = f_read(file, (uint8_t *)specs, specsSize, &read);
if (result != FR_OK || read != specsSize) {
free(specs);
f_close(file);
free(file);
return false;
}

auto data = (uint8_t *)malloc(rangeHeader.dataSize);
if (!data) {
free(specs);
f_close(file);
free(file);
return false;
}

result = f_read(file, (uint8_t *)data, rangeHeader.dataSize, &read);
if (result != FR_OK || read != rangeHeader.dataSize) {
free(specs);
free(data);
f_close(file);
free(file);
return false;
}

auto bitmapData = decodeBitmapData(data);
#if LCD_ORIENTATION == 180
flipBitmapData(bitmapData);
#elif LCD_ORIENTATION == 270
rotateBitmapData(bitmapData);
#endif
ranges.push_back({rangeHeader.begin, rangeHeader.end, bitmapData, specs});
}

f_close(file);
free(file);

return true;
}
Loading