mirror of
https://github.com/fltk/fltk.git
synced 2026-05-21 22:51:41 +08:00
Android: added font access into Android package via Assets, added font
fallbacks, added emergency font as an asset, added graceful
behavior when absolutely no font could be loaded.
Next: add other font related calls, add clipping
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12767 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
+22
-13
@@ -1,20 +1,21 @@
|
||||
|
||||
README.Android.txt - Building and using FLTK with Android Studio 3
|
||||
------------------------------------------------------------------
|
||||
==================================================================
|
||||
|
||||
|
||||
WARNING: FLTK FOR ANDROID IS WORK IN PROGRESS IN A PRETTY EARLY STAGE.
|
||||
|
||||
|
||||
CONTENTS
|
||||
==========
|
||||
CONTENTS
|
||||
--------
|
||||
|
||||
1 Building FLTK with Android Studio 3
|
||||
2 Limitation of FLTK on Android
|
||||
2 Extensions and limitation of FLTK on Android
|
||||
3 DOCUMENT HISTORY
|
||||
|
||||
|
||||
BUILDING FLTK SAMPLE WITH ANDROID STUDIO 3
|
||||
============================================
|
||||
BUILDING FLTK SAMPLE WITH ANDROID STUDIO 3
|
||||
------------------------------------------
|
||||
|
||||
There is no need to ever write a single line of Java.
|
||||
|
||||
@@ -32,19 +33,27 @@ device, you are ready to install FLTK.
|
||||
- click "run"; the project should compile and run out of the box
|
||||
|
||||
|
||||
Limitation of FLTK on Android
|
||||
===============================
|
||||
Extensions and limitation of FLTK on Android
|
||||
--------------------------------------------
|
||||
|
||||
Android support for FLTK is in a very early stage. As of March 2018, very basic
|
||||
rendering work and mouse clicks are detected.
|
||||
rendering works, text rendering work, clipping works, window layering works,
|
||||
and mouse clicks (touch events) are detected.
|
||||
|
||||
- the screen size is currently always 600x800 and is scaled up
|
||||
- there is no support for multiple windows (yet)
|
||||
When loading fonts:
|
||||
- font names starting with a $ will have the system font path inserted
|
||||
- font names starting with an @ will be loaded via the Asset Manager
|
||||
- all other names will be used verbatim
|
||||
|
||||
Limitations:
|
||||
- the screen size is currently fixed to 600x800 and is scaled up
|
||||
- many many big and little functions are not yet implemented
|
||||
|
||||
|
||||
DOCUMENT HISTORY
|
||||
==================
|
||||
DOCUMENT HISTORY
|
||||
----------------
|
||||
|
||||
Mar 17 2018 - matt: added Android extensions for fonts
|
||||
Mar 12 2018 - matt: started list of limitation that serevs as information to the
|
||||
user as much as a todo list for core developers
|
||||
Mar 6 2018 - matt: moved project to ide/AndroidStudio3/
|
||||
|
||||
Binary file not shown.
@@ -145,6 +145,9 @@ public:
|
||||
static void pre_exec_cmd(int8_t cmd);
|
||||
static void post_exec_cmd(int8_t cmd);
|
||||
static int destroy_requested() { return pDestroyRequested; }
|
||||
static const char *get_internal_data_path() { return pActivity->internalDataPath; }
|
||||
static const char *get_external_data_path() { return pActivity->externalDataPath; }
|
||||
static AAssetManager *get_asset_manager() { return pActivity->assetManager; }
|
||||
|
||||
// --- event handling
|
||||
static AInputQueue *input_event_queue() { return pInputQueue; }
|
||||
|
||||
@@ -55,6 +55,11 @@ private:
|
||||
uint8_t *pFileBuffer;
|
||||
const char *pName;
|
||||
Fl_Font pFontIndex;
|
||||
bool pError;
|
||||
|
||||
bool load_font(const char *name);
|
||||
bool load_font_file(const char *name);
|
||||
bool load_font_asset(const char *name);
|
||||
|
||||
public:
|
||||
Fl_Android_Font_Source(const char *fname, Fl_Font fnum);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "Fl_Android_Application.H"
|
||||
#include <FL/fl_draw.H>
|
||||
#include <errno.h>
|
||||
#include <FL/filename.H>
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
|
||||
#include "stb_truetype.h"
|
||||
@@ -33,30 +34,36 @@
|
||||
//};
|
||||
|
||||
|
||||
// TODO: font names starting with a $ will have the system font path inserted
|
||||
// TODO: font names starting with a . or / have a known path and will not change
|
||||
// TODO: font names starting with a ~ will have the package resource path for fonts added
|
||||
/**
|
||||
* - font names starting with a $ will have the system font path inserted
|
||||
* - font names starting with an @ will be loaded via the Asset Manager
|
||||
* - all other names will be used verbatim
|
||||
*/
|
||||
static Fl_Fontdesc built_in_table[] = {
|
||||
{"Roboto-Regular"},
|
||||
{"Roboto-Bold"},
|
||||
{"Roboto-Italic"},
|
||||
{"Roboto-BoldItalic"},
|
||||
{"CutiveMono"},
|
||||
{"CutiveMono"}, // sorry no bold
|
||||
{"CutiveMono"}, // sorry no italic
|
||||
{"CutiveMono"}, // sorry no bold-italic
|
||||
{"NotoSerif-Regular"},
|
||||
{"NotoSerif-Bold"},
|
||||
{"NotoSerif-Italic"},
|
||||
{"NotoSerif-BoldItalic"},
|
||||
{"Roboto-Regular"},
|
||||
{"DroidSansMono"},
|
||||
{"DroidSansMono"}, // sorry no bold
|
||||
{"Roboto-Regular"},
|
||||
{"$Roboto-Regular.ttf"},
|
||||
{"$Roboto-Bold.ttf"},
|
||||
{"$Roboto-Italic.ttf"},
|
||||
{"$Roboto-BoldItalic.ttf"},
|
||||
{"$CutiveMono.ttf"},
|
||||
{"$CutiveMono.ttf"}, // sorry no bold
|
||||
{"$CutiveMono.ttf"}, // sorry no italic
|
||||
{"$CutiveMono.ttf"}, // sorry no bold-italic
|
||||
{"$NotoSerif-Regular.ttf"},
|
||||
{"$NotoSerif-Bold.ttf"},
|
||||
{"$NotoSerif-Italic.ttf"},
|
||||
{"$NotoSerif-BoldItalic.ttf"},
|
||||
{"$Roboto-Regular.ttf"},
|
||||
{"$DroidSansMono.ttf"},
|
||||
{"$DroidSansMono.ttf"}, // sorry no bold
|
||||
{"$Roboto-Regular.ttf"},
|
||||
};
|
||||
|
||||
Fl_Fontdesc* fl_fonts = built_in_table;
|
||||
|
||||
static char *old_font_names[] = {
|
||||
"$DroidSans.ttf", "$DroidSerif-Regular.ttf",
|
||||
"$DroidSansMono.ttf", "$DroidSansMono.ttf"
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an empty Bytemap.
|
||||
@@ -85,7 +92,8 @@ Fl_Android_Bytemap::~Fl_Android_Bytemap()
|
||||
Fl_Android_Font_Source::Fl_Android_Font_Source(const char *fname, Fl_Font fnum) :
|
||||
pFileBuffer(0L),
|
||||
pName(fname),
|
||||
pFontIndex(fnum)
|
||||
pFontIndex(fnum),
|
||||
pError(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -98,26 +106,129 @@ Fl_Android_Font_Source::~Fl_Android_Font_Source()
|
||||
// pFont does not allocate any buffers and needs no destructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to find an load a font file.
|
||||
* @param name file or asset name
|
||||
* @return
|
||||
*/
|
||||
bool Fl_Android_Font_Source::load_font(const char *name)
|
||||
{
|
||||
if (pFileBuffer) return true;
|
||||
if (!name) return false;
|
||||
bool ret = false;
|
||||
if (name[0]=='@')
|
||||
ret = load_font_asset(name+1);
|
||||
else
|
||||
ret = load_font_file(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load a font through the asset manager.
|
||||
* @param name file or asset name
|
||||
* @return
|
||||
*/
|
||||
bool Fl_Android_Font_Source::load_font_asset(const char *name)
|
||||
{
|
||||
errno = 0;
|
||||
AAssetManager *aMgr = Fl_Android_Application::get_asset_manager();
|
||||
AAsset *aFile = AAssetManager_open(aMgr, name, AASSET_MODE_STREAMING);
|
||||
if (aFile == NULL) {
|
||||
Fl_Android_Application::log_w("Can't open font asset at '%s': ",
|
||||
name, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
size_t fsize = (size_t)AAsset_getLength(aFile);
|
||||
if (fsize == 0) {
|
||||
Fl_Android_Application::log_w("Can't read font asset at '%s': file is empty",
|
||||
name);
|
||||
AAsset_close(aFile);
|
||||
return false;
|
||||
}
|
||||
pFileBuffer = (uint8_t *)malloc(fsize);
|
||||
if (AAsset_read(aFile, pFileBuffer, fsize)<=0) {
|
||||
Fl_Android_Application::log_w("Can't read font asset at '%s': ",
|
||||
name, strerror(errno));
|
||||
free(pFileBuffer);
|
||||
pFileBuffer = 0;
|
||||
AAsset_close(aFile);
|
||||
return false;
|
||||
}
|
||||
AAsset_close(aFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load a font through the asset manager.
|
||||
* @param name file or asset name
|
||||
* @return
|
||||
*/
|
||||
bool Fl_Android_Font_Source::load_font_file(const char *name)
|
||||
{
|
||||
char buf[2048];
|
||||
if (name[0] == '$') {
|
||||
// use the system path for fonts
|
||||
snprintf(buf, 2048, "/system/fonts/%s", name + 1);
|
||||
} else {
|
||||
strcpy(buf, name);
|
||||
}
|
||||
FILE *f = fopen(buf, "rb");
|
||||
if (f == NULL) {
|
||||
Fl_Android_Application::log_w("Can't open font file at '%s': ",
|
||||
name, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t fsize = (size_t)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (fsize == 0) {
|
||||
Fl_Android_Application::log_w(
|
||||
"Can't read font file at '%s': file is empty",
|
||||
name);
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
pFileBuffer = (uint8_t *)malloc(fsize);
|
||||
if (fread(pFileBuffer, 1, fsize, f)<=0) {
|
||||
Fl_Android_Application::log_w("Can't read font file at '%s': ",
|
||||
name, strerror(errno));
|
||||
free(pFileBuffer);
|
||||
pFileBuffer = 0;
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a True Type font file and initialize the TTF interpreter.
|
||||
* A copy of the font file must remain in memory for the interpreter to work.
|
||||
*/
|
||||
void Fl_Android_Font_Source::load_font()
|
||||
{
|
||||
if (pError) return;
|
||||
if (pFileBuffer==0) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "/system/fonts/%s.ttf", fl_fonts[pFontIndex].name);
|
||||
FILE *f = fopen(buf, "rb");
|
||||
if (f==NULL) {
|
||||
Fl_Android_Application::log_e("ERROR reading font %d from '%s'!", errno, buf);
|
||||
const char *name = fl_fonts[pFontIndex].name;
|
||||
|
||||
// first attempt, try to read a font from wherever the user wishes
|
||||
bool ret = load_font(name);
|
||||
|
||||
// if that did not work, read the old style Android fonts
|
||||
if (!ret && pFontIndex<16)
|
||||
ret = load_font(old_font_names[pFontIndex/4]);
|
||||
|
||||
// if that still didn't work, see if we have the default font asset
|
||||
if (!ret)
|
||||
ret = load_font("@fonts/Roboto-Regular.ttf");
|
||||
|
||||
// still no luck? Well, I guess we can't render anything in this font.
|
||||
if (!ret) {
|
||||
Fl_Android_Application::log_e("Giving up. Can't load font '%s'", name);
|
||||
pError = true;
|
||||
return;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t fsize = (size_t)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
pFileBuffer = (uint8_t*)malloc(fsize);
|
||||
fread(pFileBuffer, 1, fsize, f);
|
||||
fclose(f);
|
||||
stbtt_InitFont(&pFont, pFileBuffer, stbtt_GetFontOffsetForIndex(pFileBuffer,0));
|
||||
}
|
||||
}
|
||||
@@ -131,51 +242,29 @@ void Fl_Android_Font_Source::load_font()
|
||||
Fl_Android_Bytemap *Fl_Android_Font_Source::get_bytemap(uint32_t c, int size)
|
||||
{
|
||||
if (pFileBuffer==0) load_font();
|
||||
if (pError) return 0L;
|
||||
|
||||
Fl_Android_Bytemap *byteMap = new Fl_Android_Bytemap();
|
||||
|
||||
#if 0
|
||||
scale = stbtt_ScaleForPixelHeight(&font, 15);
|
||||
stbtt_GetFontVMetrics(&font, &ascent,0,0);
|
||||
baseline = (int) (ascent*scale);
|
||||
|
||||
while (text[ch]) {
|
||||
int advance,lsb,x0,y0,x1,y1;
|
||||
float x_shift = xpos - (float) floor(xpos);
|
||||
stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
|
||||
stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
|
||||
stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
|
||||
// note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
|
||||
// because this API is really for baking character bitmaps into textures. if you want to render
|
||||
// a sequence of characters, you really need to render each bitmap to a temp buffer, then
|
||||
// "alpha blend" that into the working buffer
|
||||
xpos += (advance * scale);
|
||||
if (text[ch+1])
|
||||
xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
|
||||
++ch;
|
||||
}
|
||||
|
||||
#endif
|
||||
Fl_Android_Bytemap *bm = new Fl_Android_Bytemap();
|
||||
|
||||
float hgt = stbtt_ScaleForPixelHeight(&pFont, size);
|
||||
byteMap->pBytes = stbtt_GetCodepointBitmap(&pFont, 0, hgt, c,
|
||||
&byteMap->pWidth, &byteMap->pHeight,
|
||||
&byteMap->pXOffset, &byteMap->pYOffset);
|
||||
byteMap->pStride = byteMap->pWidth;
|
||||
bm->pBytes = stbtt_GetCodepointBitmap(&pFont, 0, hgt, c,
|
||||
&bm->pWidth, &bm->pHeight,
|
||||
&bm->pXOffset, &bm->pYOffset);
|
||||
bm->pStride = bm->pWidth;
|
||||
|
||||
int advance, lsb;
|
||||
stbtt_GetCodepointHMetrics(&pFont, c, &advance, &lsb);
|
||||
float scale = stbtt_ScaleForPixelHeight(&pFont, size);
|
||||
byteMap->pAdvance = (int)((scale * advance)+0.5f);
|
||||
bm->pAdvance = (int)((scale * advance)+0.5f);
|
||||
|
||||
return byteMap;
|
||||
return bm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the character in pixels.
|
||||
* This is not a good function because character advance also depends on kerning
|
||||
* which takes the next character in a text line into account. Also, FLTK is
|
||||
* limited to interger character positions, and so is the Android driver.
|
||||
* limited to integer character positions, and so is the Android driver.
|
||||
* @param c unicode character
|
||||
* @param size height in pixels
|
||||
* @return width in pixels to the start of the next character
|
||||
@@ -185,6 +274,8 @@ float Fl_Android_Font_Source::get_advance(uint32_t c, Fl_Fontsize size)
|
||||
int advance, lsb;
|
||||
|
||||
if (pFileBuffer==0) load_font();
|
||||
if (pError) return 0.0f;
|
||||
|
||||
stbtt_GetCodepointHMetrics(&pFont, c, &advance, &lsb);
|
||||
float scale = stbtt_ScaleForPixelHeight(&pFont, size);
|
||||
return scale * advance;
|
||||
@@ -236,7 +327,7 @@ Fl_Android_Font_Descriptor::~Fl_Android_Font_Descriptor()
|
||||
*/
|
||||
float Fl_Android_Font_Descriptor::get_advance(uint32_t c)
|
||||
{
|
||||
// TODO: should we cache the advance value inside the bytemap?
|
||||
// TODO: should we use the cahced value in the Bytemap?
|
||||
return pFontSource->get_advance(c, size);
|
||||
}
|
||||
|
||||
@@ -257,12 +348,12 @@ Fl_Android_Bytemap *Fl_Android_Font_Descriptor::get_bytemap(uint32_t c)
|
||||
bm = pBytemapTable.at(c);
|
||||
} catch(...) {
|
||||
bm = pFontSource->get_bytemap(c, size);
|
||||
pBytemapTable[c] = bm;
|
||||
if (bm)
|
||||
pBytemapTable[c] = bm;
|
||||
}
|
||||
return bm;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find or create a font descriptor for a given font and height.
|
||||
* @param fnum index into fl_fonts
|
||||
@@ -288,7 +379,6 @@ Fl_Android_Font_Descriptor* Fl_Android_Font_Descriptor::find(Fl_Font fnum, Fl_Fo
|
||||
return af;
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
@@ -317,6 +407,7 @@ int Fl_Android_Graphics_Driver::render_letter(int xx, int yy, uint32_t c)
|
||||
if (!fd) return xx; // this should not happen
|
||||
|
||||
Fl_Android_Bytemap *bm = fd->get_bytemap(c);
|
||||
if (!bm) return oxx;
|
||||
|
||||
// rrrr.rggg.gggb.bbbb
|
||||
xx += bm->pXOffset; yy += bm->pYOffset;
|
||||
@@ -371,10 +462,14 @@ void Fl_Android_Graphics_Driver::draw_unscaled(const char* str, int n, int x, in
|
||||
unsigned uniChar = fl_utf8decode(str + i, e, &incr);
|
||||
int x1 = x;
|
||||
x = render_letter(x, y, uniChar);
|
||||
#if 0
|
||||
// use this to make the character baseline visible
|
||||
Fl_Color old = fl_color();
|
||||
fl_color(FL_RED);
|
||||
fl_xyline(x1, y, x);
|
||||
fl_yxline(x1, y-5, y+5);
|
||||
fl_color(old);
|
||||
#endif
|
||||
i += incr;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user