mirror of https://github.com/buggins/dlangui.git
fonts refactoring; support tabs
This commit is contained in:
parent
31794b9df6
commit
ca094497a1
|
@ -26,11 +26,17 @@ import std.algorithm;
|
||||||
|
|
||||||
/// font family
|
/// font family
|
||||||
enum FontFamily : ubyte {
|
enum FontFamily : ubyte {
|
||||||
|
/// Unknown / not set / does not matter
|
||||||
Unspecified,
|
Unspecified,
|
||||||
|
/// Sans Serif font, e.g. Arial
|
||||||
SansSerif,
|
SansSerif,
|
||||||
|
/// Serif font, e.g. Times New Roman
|
||||||
Serif,
|
Serif,
|
||||||
|
/// Fantasy font
|
||||||
Fantasy,
|
Fantasy,
|
||||||
|
/// Cursive font
|
||||||
Cursive,
|
Cursive,
|
||||||
|
/// Monospace font (fixed pitch font), e.g. Courier New
|
||||||
MonoSpace
|
MonoSpace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,11 +46,12 @@ enum FontWeight : int {
|
||||||
Bold = 800
|
Bold = 800
|
||||||
}
|
}
|
||||||
|
|
||||||
const dchar UNICODE_SOFT_HYPHEN_CODE = 0x00ad;
|
immutable dchar UNICODE_SOFT_HYPHEN_CODE = 0x00ad;
|
||||||
const dchar UNICODE_ZERO_WIDTH_SPACE = 0x200b;
|
immutable dchar UNICODE_ZERO_WIDTH_SPACE = 0x200b;
|
||||||
const dchar UNICODE_NO_BREAK_SPACE = 0x00a0;
|
immutable dchar UNICODE_NO_BREAK_SPACE = 0x00a0;
|
||||||
const dchar UNICODE_HYPHEN = 0x2010;
|
immutable dchar UNICODE_HYPHEN = 0x2010;
|
||||||
const dchar UNICODE_NB_HYPHEN = 0x2011;
|
immutable dchar UNICODE_NB_HYPHEN = 0x2011;
|
||||||
|
|
||||||
|
|
||||||
version (USE_OPENGL) {
|
version (USE_OPENGL) {
|
||||||
private __gshared void function(uint id) _glyphDestroyCallback;
|
private __gshared void function(uint id) _glyphDestroyCallback;
|
||||||
|
@ -54,15 +61,27 @@ version (USE_OPENGL) {
|
||||||
@property void glyphDestroyCallback(void function(uint id) callback) { _glyphDestroyCallback = callback; }
|
@property void glyphDestroyCallback(void function(uint id) callback) { _glyphDestroyCallback = callback; }
|
||||||
|
|
||||||
private __gshared uint _nextGlyphId;
|
private __gshared uint _nextGlyphId;
|
||||||
|
/// ID generator for glyphs
|
||||||
uint nextGlyphId() { return _nextGlyphId++; }
|
uint nextGlyphId() { return _nextGlyphId++; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// glyph image cache
|
/***************************************
|
||||||
|
* Glyph image cache
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Recently used glyphs are marked with glyph.lastUsage = 1
|
||||||
|
*
|
||||||
|
* checkpoint() call clears usage marks
|
||||||
|
*
|
||||||
|
* cleanup() removes all items not accessed since last checkpoint()
|
||||||
|
*
|
||||||
|
***************************************/
|
||||||
struct GlyphCache
|
struct GlyphCache
|
||||||
{
|
{
|
||||||
alias glyph_ptr = Glyph*;
|
alias glyph_ptr = Glyph*;
|
||||||
private glyph_ptr[][1024] _glyphs;
|
private glyph_ptr[][1024] _glyphs;
|
||||||
|
|
||||||
|
/// try to find glyph for character in cache, returns null if not found
|
||||||
Glyph * find(dchar ch) {
|
Glyph * find(dchar ch) {
|
||||||
ch = ch & 0xF_FFFF;
|
ch = ch & 0xF_FFFF;
|
||||||
//if (_array is null)
|
//if (_array is null)
|
||||||
|
@ -78,7 +97,8 @@ struct GlyphCache
|
||||||
res.lastUsage = 1;
|
res.lastUsage = 1;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
/// put glyph to cache
|
|
||||||
|
/// put character glyph to cache
|
||||||
Glyph * put(dchar ch, Glyph * glyph) {
|
Glyph * put(dchar ch, Glyph * glyph) {
|
||||||
ch = ch & 0xF_FFFF;
|
ch = ch & 0xF_FFFF;
|
||||||
uint p = ch >> 8;
|
uint p = ch >> 8;
|
||||||
|
@ -108,7 +128,7 @@ struct GlyphCache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear usage flags for all entries
|
/// clear usage flags for all entries
|
||||||
void checkpoint() {
|
void checkpoint() {
|
||||||
foreach(part; _glyphs) {
|
foreach(part; _glyphs) {
|
||||||
if (part !is null)
|
if (part !is null)
|
||||||
|
@ -119,7 +139,7 @@ struct GlyphCache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// removes all entries
|
/// removes all entries (notify OpenGL cache about removed glyphs)
|
||||||
void clear() {
|
void clear() {
|
||||||
foreach(part; _glyphs) {
|
foreach(part; _glyphs) {
|
||||||
if (part !is null)
|
if (part !is null)
|
||||||
|
@ -136,174 +156,12 @@ struct GlyphCache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// on destroy, destroy all items (notify OpenGL cache about removed glyphs)
|
||||||
~this() {
|
~this() {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
/// font glyph cache
|
|
||||||
struct GlyphCache
|
|
||||||
{
|
|
||||||
//Glyph[ushort] _map;
|
|
||||||
Glyph[][256] _glyphs;
|
|
||||||
|
|
||||||
//Glyph[] _array;
|
|
||||||
|
|
||||||
// find glyph in cache
|
|
||||||
Glyph * find(ushort glyphIndex) {
|
|
||||||
//if (_array is null)
|
|
||||||
// _array = new Glyph[0x10000];
|
|
||||||
ushort p = glyphIndex >> 8;
|
|
||||||
if (_glyphs[p] is null)
|
|
||||||
return null;
|
|
||||||
ushort i = glyphIndex & 0xFF;
|
|
||||||
if (_glyphs[p][i].glyphIndex == 0)
|
|
||||||
return null;
|
|
||||||
return &_glyphs[p][i];
|
|
||||||
//if (_array[glyphIndex].glyphIndex)
|
|
||||||
// return &_array[glyphIndex];
|
|
||||||
//return null;
|
|
||||||
|
|
||||||
//Glyph * res = (glyphIndex in _map);
|
|
||||||
//if (res !is null)
|
|
||||||
// res.lastUsage = 1;
|
|
||||||
//return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// put glyph to cache
|
|
||||||
Glyph * put(ushort glyphIndex, Glyph * glyph) {
|
|
||||||
ushort p = glyphIndex >> 8;
|
|
||||||
ushort i = glyphIndex & 0xFF;
|
|
||||||
if (_glyphs[p] is null)
|
|
||||||
_glyphs[p] = new Glyph[256];
|
|
||||||
_glyphs[p][i] = *glyph;
|
|
||||||
return &_glyphs[p][i]; // = *glyph;
|
|
||||||
|
|
||||||
//_array[glyphIndex] = *glyph;
|
|
||||||
//return &_array[glyphIndex];
|
|
||||||
|
|
||||||
//_map[glyphIndex] = *glyph;
|
|
||||||
//Glyph * res = glyphIndex in _map;
|
|
||||||
//res.lastUsage = 1;
|
|
||||||
//return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear usage flags for all entries
|
|
||||||
void checkpoint() {
|
|
||||||
//foreach(ref Glyph item; _map) {
|
|
||||||
// item.lastUsage = 0;
|
|
||||||
//}
|
|
||||||
foreach(ref Glyph[] part; _glyphs) {
|
|
||||||
if (part !is null)
|
|
||||||
foreach(ref Glyph item; part) {
|
|
||||||
item.lastUsage = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//foreach(ref Glyph item; _array) {
|
|
||||||
// item.lastUsage = 0;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// removes entries not used after last call of checkpoint() or cleanup()
|
|
||||||
void cleanup() {
|
|
||||||
//uint dst = 0;
|
|
||||||
// notify about destroyed glyphs
|
|
||||||
version (USE_OPENGL) {
|
|
||||||
if (_glyphDestroyCallback !is null) {
|
|
||||||
foreach(ref Glyph[] part; _glyphs) {
|
|
||||||
if (part !is null)
|
|
||||||
foreach(ref Glyph item; part) {
|
|
||||||
if (item.lastUsage == 0 && item.glyphIndex)
|
|
||||||
_glyphDestroyCallback(item.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//foreach(ref Glyph item; _map) {
|
|
||||||
// if (item.lastUsage == 0)
|
|
||||||
// _glyphDestroyCallback(item.id);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//ushort[] forDelete;
|
|
||||||
//foreach(ref Glyph item; _map)
|
|
||||||
// if (item.lastUsage == 0)
|
|
||||||
// forDelete ~= item.glyphIndex;
|
|
||||||
//foreach(ushort index; forDelete)
|
|
||||||
// _map.remove(index);
|
|
||||||
foreach(ref Glyph[] part; _glyphs) {
|
|
||||||
if (part !is null)
|
|
||||||
foreach(ref Glyph item; part) {
|
|
||||||
if (item.lastUsage == 0 && item.glyphIndex) {
|
|
||||||
item.glyphIndex = 0;
|
|
||||||
item.glyph = null;
|
|
||||||
version (USE_OPENGL) {
|
|
||||||
item.id = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//foreach(ref Glyph item; _array) {
|
|
||||||
// if (item.lastUsage == 0 && item.glyphIndex) {
|
|
||||||
// item.glyphIndex = 0;
|
|
||||||
// item.glyph = null;
|
|
||||||
// item.id = 0;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// removes all entries
|
|
||||||
void clear() {
|
|
||||||
// notify about destroyed glyphs
|
|
||||||
version (USE_OPENGL) {
|
|
||||||
if (_glyphDestroyCallback !is null) {
|
|
||||||
foreach(ref Glyph[] part; _glyphs) {
|
|
||||||
if (part !is null)
|
|
||||||
foreach(ref Glyph item; part) {
|
|
||||||
if (item.glyphIndex)
|
|
||||||
_glyphDestroyCallback(item.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach(ref Glyph[] part; _glyphs) {
|
|
||||||
if (part !is null)
|
|
||||||
foreach(ref Glyph item; part) {
|
|
||||||
if (item.glyphIndex) {
|
|
||||||
item.glyphIndex = 0;
|
|
||||||
item.glyph = null;
|
|
||||||
version (USE_OPENGL) {
|
|
||||||
item.id = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//version (USE_OPENGL) {
|
|
||||||
// if (_glyphDestroyCallback !is null) {
|
|
||||||
// foreach(ref Glyph item; _array) {
|
|
||||||
// if (item.glyphIndex)
|
|
||||||
// _glyphDestroyCallback(item.id);
|
|
||||||
// }
|
|
||||||
// //foreach(ref Glyph item; _map) {
|
|
||||||
// // if (item.lastUsage == 0)
|
|
||||||
// // _glyphDestroyCallback(item.id);
|
|
||||||
// //}
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
////_map.clear();
|
|
||||||
//foreach(ref Glyph item; _array) {
|
|
||||||
// if (item.glyphIndex) {
|
|
||||||
// item.glyphIndex = 0;
|
|
||||||
// item.glyph = null;
|
|
||||||
// item.id = 0;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
~this() {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// Font object
|
/// Font object
|
||||||
class Font : RefCountedObject {
|
class Font : RefCountedObject {
|
||||||
|
@ -350,23 +208,130 @@ class Font : RefCountedObject {
|
||||||
return !g ? 0 : g.width;
|
return !g ? 0 : g.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars.
|
/*******************************************************************************************
|
||||||
abstract int measureText(const dchar[] text, ref int[] widths, int maxWidth);
|
* Measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* text = text string to measure
|
||||||
|
* widths = output buffer to put measured widths (widths[i] will be set to cumulative widths text[0..i])
|
||||||
|
* maxWidth = maximum width - measure is stopping if max width is reached
|
||||||
|
* tabSize = tabulation size, in number of spaces
|
||||||
|
* tabOffset = when string is drawn not from left position, use to move tab stops left/right
|
||||||
|
******************************************************************************************/
|
||||||
|
int measureText(const dchar[] text, ref int[] widths, int maxWidth=int.max, int tabSize = 4, int tabOffset = 0) {
|
||||||
|
if (text.length == 0)
|
||||||
|
return 0;
|
||||||
|
const dchar * pstr = text.ptr;
|
||||||
|
uint len = cast(uint)text.length;
|
||||||
|
int x = 0;
|
||||||
|
int charsMeasured = 0;
|
||||||
|
int * pwidths = widths.ptr;
|
||||||
|
int tabWidth = spaceWidth * tabSize; // width of full tab in pixels
|
||||||
|
tabOffset = tabOffset % tabWidth;
|
||||||
|
if (tabOffset < 0)
|
||||||
|
tabOffset += tabWidth;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
//auto measureStart = std.datetime.Clock.currAppTick;
|
||||||
|
dchar ch = pstr[i];
|
||||||
|
if (ch == '\t') {
|
||||||
|
// measure tab
|
||||||
|
int tabPosition = (x + tabWidth - tabOffset) / tabWidth * tabWidth + tabOffset;
|
||||||
|
while (tabPosition < x + spaceWidth)
|
||||||
|
tabPosition += tabWidth;
|
||||||
|
pwidths[i] = tabPosition;
|
||||||
|
charsMeasured = i + 1;
|
||||||
|
x = tabPosition;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Glyph * glyph = getCharGlyph(pstr[i], true); // TODO: what is better
|
||||||
|
//auto measureEnd = std.datetime.Clock.currAppTick;
|
||||||
|
//auto duration = measureEnd - measureStart;
|
||||||
|
//if (duration.length > 10)
|
||||||
|
// Log.d("ft measureText took ", duration.length, " ticks");
|
||||||
|
if (glyph is null) {
|
||||||
|
// if no glyph, use previous width - treat as zero width
|
||||||
|
pwidths[i] = x;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int w = x + glyph.width; // using advance
|
||||||
|
int w2 = x + glyph.originX + glyph.blackBoxX; // using black box
|
||||||
|
if (w < w2) // choose bigger value
|
||||||
|
w = w2;
|
||||||
|
pwidths[i] = w;
|
||||||
|
x += glyph.width;
|
||||||
|
charsMeasured = i + 1;
|
||||||
|
if (x > maxWidth)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return charsMeasured;
|
||||||
|
}
|
||||||
|
|
||||||
private int[] _textSizeBuffer; // buffer to reuse while measuring strings - to avoid GC
|
private int[] _textSizeBuffer; // buffer to reuse while measuring strings - to avoid GC
|
||||||
/// measure text string as single line, returns width and height
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Measure text string as single line, returns width and height
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* text = text string to measure
|
||||||
|
* maxWidth = maximum width - measure is stopping if max width is reached
|
||||||
|
************************************************************************/
|
||||||
Point textSize(const dchar[] text, int maxWidth = int.max) {
|
Point textSize(const dchar[] text, int maxWidth = int.max) {
|
||||||
if (_textSizeBuffer.length < text.length + 1)
|
if (_textSizeBuffer.length < text.length + 1)
|
||||||
_textSizeBuffer.length = text.length + 1;
|
_textSizeBuffer.length = text.length + 1;
|
||||||
//int[] widths = new int[text.length + 1];
|
|
||||||
int charsMeasured = measureText(text, _textSizeBuffer, maxWidth);
|
int charsMeasured = measureText(text, _textSizeBuffer, maxWidth);
|
||||||
if (charsMeasured < 1)
|
if (charsMeasured < 1)
|
||||||
return Point(0,0);
|
return Point(0,0);
|
||||||
return Point(_textSizeBuffer[charsMeasured - 1], height);
|
return Point(_textSizeBuffer[charsMeasured - 1], height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// draw text string to buffer
|
/*****************************************************************************************
|
||||||
abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color);
|
* Draw text string to buffer.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* buf = graphics buffer to draw text to
|
||||||
|
* x = x coordinate to draw first character at
|
||||||
|
* y = y coordinate to draw first character at
|
||||||
|
* text = text string to draw
|
||||||
|
* color = color for drawing of glyphs
|
||||||
|
* tabSize = tabulation size, in number of spaces
|
||||||
|
* tabOffset = when string is drawn not from left position, use to move tab stops left/right
|
||||||
|
****************************************************************************************/
|
||||||
|
void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color, int tabSize = 4, int tabOffset = 0) {
|
||||||
|
if (text.length == 0)
|
||||||
|
return; // nothing to draw - empty text
|
||||||
|
if (_textSizeBuffer.length < text.length)
|
||||||
|
_textSizeBuffer.length = text.length;
|
||||||
|
int charsMeasured = measureText(text, _textSizeBuffer, int.max, tabSize, tabOffset);
|
||||||
|
Rect clip = buf.clipOrFullRect;
|
||||||
|
if (clip.empty)
|
||||||
|
return; // not visible - clipped out
|
||||||
|
if (y + height < clip.top || y >= clip.bottom)
|
||||||
|
return; // not visible - fully above or below clipping rectangle
|
||||||
|
int _baseline = baseline;
|
||||||
|
for (int i = 0; i < charsMeasured; i++) {
|
||||||
|
int xx = (i > 0) ? _textSizeBuffer[i - 1] : 0;
|
||||||
|
if (x + xx > clip.right)
|
||||||
|
break;
|
||||||
|
if (x + xx + 255 < clip.left)
|
||||||
|
continue; // far at left of clipping region
|
||||||
|
dchar ch = text[i];
|
||||||
|
if (ch == ' ' || ch == '\t')
|
||||||
|
continue;
|
||||||
|
Glyph * glyph = getCharGlyph(ch);
|
||||||
|
if (glyph is null)
|
||||||
|
continue;
|
||||||
|
if ( glyph.blackBoxX && glyph.blackBoxY ) {
|
||||||
|
int gx = x + xx + glyph.originX;
|
||||||
|
if (gx + glyph.blackBoxX < clip.left)
|
||||||
|
continue;
|
||||||
|
buf.drawGlyph( gx,
|
||||||
|
y + _baseline - glyph.originY,
|
||||||
|
glyph,
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// get character glyph information
|
/// get character glyph information
|
||||||
abstract Glyph * getCharGlyph(dchar ch, bool withImage = true);
|
abstract Glyph * getCharGlyph(dchar ch, bool withImage = true);
|
||||||
|
|
||||||
|
|
|
@ -368,64 +368,7 @@ class FreeTypeFont : Font {
|
||||||
return glyph;
|
return glyph;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw text string to buffer
|
/// load font files
|
||||||
override void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color) {
|
|
||||||
int[] widths;
|
|
||||||
int bl = baseline;
|
|
||||||
int xx = 0;
|
|
||||||
for (int i = 0; i < text.length; i++) {
|
|
||||||
Glyph * glyph = getCharGlyph(text[i], true);
|
|
||||||
if (glyph is null)
|
|
||||||
continue;
|
|
||||||
if ( glyph.blackBoxX && glyph.blackBoxY ) {
|
|
||||||
int x0 = x + xx + glyph.originX;
|
|
||||||
int y0 = y + bl - glyph.originY;
|
|
||||||
if (x0 > buf.width)
|
|
||||||
break; // outside right bound
|
|
||||||
Rect rc = Rect(x0, y0, x0 + glyph.blackBoxX, y0 + glyph.blackBoxY);
|
|
||||||
if (buf.applyClipping(rc))
|
|
||||||
buf.drawGlyph( x0,
|
|
||||||
y0,
|
|
||||||
glyph,
|
|
||||||
color);
|
|
||||||
}
|
|
||||||
xx += glyph.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override int measureText(const dchar[] text, ref int[] widths, int maxWidth) {
|
|
||||||
if (text.length == 0)
|
|
||||||
return 0;
|
|
||||||
const dchar * pstr = text.ptr;
|
|
||||||
uint len = cast(uint)text.length;
|
|
||||||
int x = 0;
|
|
||||||
int charsMeasured = 0;
|
|
||||||
int * pwidths = widths.ptr;
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
//auto measureStart = std.datetime.Clock.currAppTick;
|
|
||||||
Glyph * glyph = getCharGlyph(pstr[i], true); // TODO: what is better
|
|
||||||
//auto measureEnd = std.datetime.Clock.currAppTick;
|
|
||||||
//auto duration = measureEnd - measureStart;
|
|
||||||
//if (duration.length > 10)
|
|
||||||
// Log.d("ft measureText took ", duration.length, " ticks");
|
|
||||||
if (glyph is null) {
|
|
||||||
// if no glyph, use previous width - treat as zero width
|
|
||||||
pwidths[i] = i > 0 ? pwidths[i-1] : 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int w = x + glyph.width; // using advance
|
|
||||||
int w2 = x + glyph.originX + glyph.blackBoxX; // using black box
|
|
||||||
if (w < w2) // choose bigger value
|
|
||||||
w = w2;
|
|
||||||
pwidths[i] = w;
|
|
||||||
x += glyph.width;
|
|
||||||
charsMeasured = i + 1;
|
|
||||||
if (x > maxWidth)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return charsMeasured;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool create() {
|
bool create() {
|
||||||
if (!isNull())
|
if (!isNull())
|
||||||
clear();
|
clear();
|
||||||
|
@ -438,12 +381,12 @@ class FreeTypeFont : Font {
|
||||||
return _files.length > 0;
|
return _files.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear usage flags for all entries
|
/// clear usage flags for all entries
|
||||||
override void checkpoint() {
|
override void checkpoint() {
|
||||||
_glyphCache.checkpoint();
|
_glyphCache.checkpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// removes entries not used after last call of checkpoint() or cleanup()
|
/// removes entries not used after last call of checkpoint() or cleanup()
|
||||||
override void cleanup() {
|
override void cleanup() {
|
||||||
_glyphCache.cleanup();
|
_glyphCache.cleanup();
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,98 +185,8 @@ class Win32Font : Font {
|
||||||
return _glyphCache.put(ch, g);
|
return _glyphCache.put(ch, g);
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw text string to buffer
|
|
||||||
override void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color) {
|
|
||||||
int[] widths;
|
|
||||||
int charsMeasured = measureText(text, widths, 3000);
|
|
||||||
Rect clip = buf.clipOrFullRect;
|
|
||||||
if (y + height < clip.top || y >= clip.bottom)
|
|
||||||
return;
|
|
||||||
for (int i = 0; i < charsMeasured; i++) {
|
|
||||||
int xx = (i > 0) ? widths[i - 1] : 0;
|
|
||||||
if (x + xx > clip.right)
|
|
||||||
break;
|
|
||||||
Glyph * glyph = getCharGlyph(text[i]);
|
|
||||||
if (glyph is null)
|
|
||||||
continue;
|
|
||||||
if ( glyph.blackBoxX && glyph.blackBoxY ) {
|
|
||||||
int gx = x + xx + glyph.originX;
|
|
||||||
if (gx + glyph.blackBoxX < clip.left)
|
|
||||||
continue;
|
|
||||||
buf.drawGlyph( gx,
|
|
||||||
y + _baseline - glyph.originY,
|
|
||||||
glyph,
|
|
||||||
color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static if (true) {
|
|
||||||
override int measureText(const dchar[] text, ref int[] widths, int maxWidth) {
|
|
||||||
if (text.length == 0)
|
|
||||||
return 0;
|
|
||||||
const dchar * pstr = text.ptr;
|
|
||||||
uint len = cast(uint)text.length;
|
|
||||||
if (widths.length < len)
|
|
||||||
widths.length = len;
|
|
||||||
int x = 0;
|
|
||||||
int charsMeasured = 0;
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
Glyph * glyph = getCharGlyph(text[i], true); // TODO: what is better
|
|
||||||
if (glyph is null) {
|
|
||||||
// if no glyph, use previous width - treat as zero width
|
|
||||||
widths[i] = i > 0 ? widths[i-1] : 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int w = x + glyph.width; // using advance
|
|
||||||
int w2 = x + glyph.originX + glyph.blackBoxX; // using black box
|
|
||||||
if (w < w2) // choose bigger value
|
|
||||||
w = w2;
|
|
||||||
widths[i] = w;
|
|
||||||
x += glyph.width;
|
|
||||||
charsMeasured = i + 1;
|
|
||||||
if (x > maxWidth)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return charsMeasured;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
override int measureText(const dchar[] text, ref int[] widths, int maxWidth) {
|
|
||||||
if (_hfont is null || _drawbuf is null || text.length == 0)
|
|
||||||
return 0;
|
|
||||||
wstring utf16text = toUTF16(text);
|
|
||||||
const wchar * pstr = utf16text.ptr;
|
|
||||||
uint len = cast(uint)utf16text.length;
|
|
||||||
GCP_RESULTSW gcpres;
|
|
||||||
gcpres.lStructSize = gcpres.sizeof;
|
|
||||||
if (widths.length < len + 1)
|
|
||||||
widths.length = len + 1;
|
|
||||||
gcpres.lpDx = widths.ptr;
|
|
||||||
gcpres.nMaxFit = len;
|
|
||||||
gcpres.nGlyphs = len;
|
|
||||||
uint res = GetCharacterPlacementW(
|
|
||||||
_drawbuf.dc,
|
|
||||||
pstr,
|
|
||||||
len,
|
|
||||||
maxWidth,
|
|
||||||
&gcpres,
|
|
||||||
GCP_MAXEXTENT); //|GCP_USEKERNING
|
|
||||||
if (!res) {
|
|
||||||
widths[0] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
uint measured = gcpres.nMaxFit;
|
|
||||||
int total = 0;
|
|
||||||
for (int i = 0; i < measured; i++) {
|
|
||||||
int w = widths[i];
|
|
||||||
total += w;
|
|
||||||
widths[i] = total;
|
|
||||||
}
|
|
||||||
return measured;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// init from font definition
|
||||||
bool create(FontDef * def, int size, int weight, bool italic) {
|
bool create(FontDef * def, int size, int weight, bool italic) {
|
||||||
if (!isNull())
|
if (!isNull())
|
||||||
clear();
|
clear();
|
||||||
|
|
Loading…
Reference in New Issue