mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 22:26:36 +00:00
Feature fontmetrics (#8978)
This commit is contained in:
parent
cd22723623
commit
1a47164876
@ -1,6 +1,7 @@
|
|||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
import functools
|
import functools
|
||||||
import hashlib
|
import hashlib
|
||||||
|
from itertools import accumulate
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -468,8 +469,9 @@ class EFont:
|
|||||||
|
|
||||||
|
|
||||||
class GlyphInfo:
|
class GlyphInfo:
|
||||||
def __init__(self, data_len, advance, offset_x, offset_y, width, height):
|
def __init__(self, glyph, data, advance, offset_x, offset_y, width, height):
|
||||||
self.data_len = data_len
|
self.glyph = glyph
|
||||||
|
self.bitmap_data = data
|
||||||
self.advance = advance
|
self.advance = advance
|
||||||
self.offset_x = offset_x
|
self.offset_x = offset_x
|
||||||
self.offset_y = offset_y
|
self.offset_y = offset_y
|
||||||
@ -477,6 +479,62 @@ class GlyphInfo:
|
|||||||
self.height = height
|
self.height = height
|
||||||
|
|
||||||
|
|
||||||
|
def glyph_to_glyphinfo(glyph, font, size, bpp):
|
||||||
|
scale = 256 // (1 << bpp)
|
||||||
|
if not font.is_scalable:
|
||||||
|
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
||||||
|
if size in sizes:
|
||||||
|
font.select_size(sizes.index(size))
|
||||||
|
else:
|
||||||
|
font.set_pixel_sizes(size, 0)
|
||||||
|
flags = FT_LOAD_RENDER
|
||||||
|
if bpp != 1:
|
||||||
|
flags |= FT_LOAD_NO_BITMAP
|
||||||
|
else:
|
||||||
|
flags |= FT_LOAD_TARGET_MONO
|
||||||
|
font.load_char(glyph, flags)
|
||||||
|
width = font.glyph.bitmap.width
|
||||||
|
height = font.glyph.bitmap.rows
|
||||||
|
buffer = font.glyph.bitmap.buffer
|
||||||
|
pitch = font.glyph.bitmap.pitch
|
||||||
|
glyph_data = [0] * ((height * width * bpp + 7) // 8)
|
||||||
|
src_mode = font.glyph.bitmap.pixel_mode
|
||||||
|
pos = 0
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
if src_mode == ft_pixel_mode_mono:
|
||||||
|
pixel = (
|
||||||
|
(1 << bpp) - 1
|
||||||
|
if buffer[y * pitch + x // 8] & (1 << (7 - x % 8))
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
pixel = buffer[y * pitch + x] // scale
|
||||||
|
for bit_num in range(bpp):
|
||||||
|
if pixel & (1 << (bpp - bit_num - 1)):
|
||||||
|
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
||||||
|
pos += 1
|
||||||
|
ascender = pt_to_px(font.size.ascender)
|
||||||
|
if ascender == 0:
|
||||||
|
if not font.is_scalable:
|
||||||
|
ascender = size
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Unable to determine ascender of font %s %s",
|
||||||
|
font.family_name,
|
||||||
|
font.style_name,
|
||||||
|
)
|
||||||
|
return GlyphInfo(
|
||||||
|
glyph,
|
||||||
|
glyph_data,
|
||||||
|
pt_to_px(font.glyph.metrics.horiAdvance),
|
||||||
|
font.glyph.bitmap_left,
|
||||||
|
ascender - font.glyph.bitmap_top,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
"""
|
"""
|
||||||
Collect all glyph codepoints, construct a map from a codepoint to a font file.
|
Collect all glyph codepoints, construct a map from a codepoint to a font file.
|
||||||
@ -506,98 +564,47 @@ async def to_code(config):
|
|||||||
|
|
||||||
codepoints = list(point_set)
|
codepoints = list(point_set)
|
||||||
codepoints.sort(key=functools.cmp_to_key(glyph_comparator))
|
codepoints.sort(key=functools.cmp_to_key(glyph_comparator))
|
||||||
glyph_args = {}
|
|
||||||
data = []
|
|
||||||
bpp = config[CONF_BPP]
|
bpp = config[CONF_BPP]
|
||||||
scale = 256 // (1 << bpp)
|
|
||||||
size = config[CONF_SIZE]
|
size = config[CONF_SIZE]
|
||||||
# create the data array for all glyphs
|
# create the data array for all glyphs
|
||||||
for codepoint in codepoints:
|
glyph_args = [
|
||||||
font = point_font_map[codepoint]
|
glyph_to_glyphinfo(x, point_font_map[x], size, bpp) for x in codepoints
|
||||||
if not font.is_scalable:
|
]
|
||||||
sizes = [pt_to_px(x.size) for x in font.available_sizes]
|
rhs = [HexInt(x) for x in flatten([x.bitmap_data for x in glyph_args])]
|
||||||
if size in sizes:
|
|
||||||
font.select_size(sizes.index(size))
|
|
||||||
else:
|
|
||||||
font.set_pixel_sizes(size, 0)
|
|
||||||
flags = FT_LOAD_RENDER
|
|
||||||
if bpp != 1:
|
|
||||||
flags |= FT_LOAD_NO_BITMAP
|
|
||||||
else:
|
|
||||||
flags |= FT_LOAD_TARGET_MONO
|
|
||||||
font.load_char(codepoint, flags)
|
|
||||||
width = font.glyph.bitmap.width
|
|
||||||
height = font.glyph.bitmap.rows
|
|
||||||
buffer = font.glyph.bitmap.buffer
|
|
||||||
pitch = font.glyph.bitmap.pitch
|
|
||||||
glyph_data = [0] * ((height * width * bpp + 7) // 8)
|
|
||||||
src_mode = font.glyph.bitmap.pixel_mode
|
|
||||||
pos = 0
|
|
||||||
for y in range(height):
|
|
||||||
for x in range(width):
|
|
||||||
if src_mode == ft_pixel_mode_mono:
|
|
||||||
pixel = (
|
|
||||||
(1 << bpp) - 1
|
|
||||||
if buffer[y * pitch + x // 8] & (1 << (7 - x % 8))
|
|
||||||
else 0
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
pixel = buffer[y * pitch + x] // scale
|
|
||||||
for bit_num in range(bpp):
|
|
||||||
if pixel & (1 << (bpp - bit_num - 1)):
|
|
||||||
glyph_data[pos // 8] |= 0x80 >> (pos % 8)
|
|
||||||
pos += 1
|
|
||||||
ascender = pt_to_px(font.size.ascender)
|
|
||||||
if ascender == 0:
|
|
||||||
if not font.is_scalable:
|
|
||||||
ascender = size
|
|
||||||
else:
|
|
||||||
_LOGGER.error(
|
|
||||||
"Unable to determine ascender of font %s", config[CONF_FILE]
|
|
||||||
)
|
|
||||||
glyph_args[codepoint] = GlyphInfo(
|
|
||||||
len(data),
|
|
||||||
pt_to_px(font.glyph.metrics.horiAdvance),
|
|
||||||
font.glyph.bitmap_left,
|
|
||||||
ascender - font.glyph.bitmap_top,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
)
|
|
||||||
data += glyph_data
|
|
||||||
|
|
||||||
rhs = [HexInt(x) for x in data]
|
|
||||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||||
|
|
||||||
# Create the glyph table that points to data in the above array.
|
# Create the glyph table that points to data in the above array.
|
||||||
glyph_initializer = []
|
glyph_initializer = [
|
||||||
for codepoint in codepoints:
|
cg.StructInitializer(
|
||||||
glyph_initializer.append(
|
GlyphData,
|
||||||
cg.StructInitializer(
|
(
|
||||||
GlyphData,
|
"a_char",
|
||||||
(
|
cg.RawExpression(f"(const uint8_t *){cpp_string_escape(x.glyph)}"),
|
||||||
"a_char",
|
),
|
||||||
cg.RawExpression(
|
(
|
||||||
f"(const uint8_t *){cpp_string_escape(codepoint)}"
|
"data",
|
||||||
),
|
cg.RawExpression(f"{str(prog_arr)} + {str(y - len(x.bitmap_data))}"),
|
||||||
),
|
),
|
||||||
(
|
("advance", x.advance),
|
||||||
"data",
|
("offset_x", x.offset_x),
|
||||||
cg.RawExpression(
|
("offset_y", x.offset_y),
|
||||||
f"{str(prog_arr)} + {str(glyph_args[codepoint].data_len)}"
|
("width", x.width),
|
||||||
),
|
("height", x.height),
|
||||||
),
|
|
||||||
("advance", glyph_args[codepoint].advance),
|
|
||||||
("offset_x", glyph_args[codepoint].offset_x),
|
|
||||||
("offset_y", glyph_args[codepoint].offset_y),
|
|
||||||
("width", glyph_args[codepoint].width),
|
|
||||||
("height", glyph_args[codepoint].height),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
for (x, y) in zip(
|
||||||
|
glyph_args, list(accumulate([len(x.bitmap_data) for x in glyph_args]))
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer)
|
glyphs = cg.static_const_array(config[CONF_RAW_GLYPH_ID], glyph_initializer)
|
||||||
|
|
||||||
font_height = pt_to_px(base_font.size.height)
|
font_height = pt_to_px(base_font.size.height)
|
||||||
ascender = pt_to_px(base_font.size.ascender)
|
ascender = pt_to_px(base_font.size.ascender)
|
||||||
|
descender = abs(pt_to_px(base_font.size.descender))
|
||||||
|
g = glyph_to_glyphinfo("x", base_font, size, bpp)
|
||||||
|
xheight = g.height if len(g.bitmap_data) > 1 else 0
|
||||||
|
g = glyph_to_glyphinfo("X", base_font, size, bpp)
|
||||||
|
capheight = g.height if len(g.bitmap_data) > 1 else 0
|
||||||
if font_height == 0:
|
if font_height == 0:
|
||||||
if not base_font.is_scalable:
|
if not base_font.is_scalable:
|
||||||
font_height = size
|
font_height = size
|
||||||
@ -610,5 +617,8 @@ async def to_code(config):
|
|||||||
len(glyph_initializer),
|
len(glyph_initializer),
|
||||||
ascender,
|
ascender,
|
||||||
font_height,
|
font_height,
|
||||||
|
descender,
|
||||||
|
xheight,
|
||||||
|
capheight,
|
||||||
bpp,
|
bpp,
|
||||||
)
|
)
|
||||||
|
@ -45,8 +45,15 @@ void Glyph::scan_area(int *x1, int *y1, int *width, int *height) const {
|
|||||||
*height = this->glyph_data_->height;
|
*height = this->glyph_data_->height;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp)
|
Font::Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
||||||
: baseline_(baseline), height_(height), bpp_(bpp) {
|
uint8_t bpp)
|
||||||
|
: baseline_(baseline),
|
||||||
|
height_(height),
|
||||||
|
descender_(descender),
|
||||||
|
linegap_(height - baseline - descender),
|
||||||
|
xheight_(xheight),
|
||||||
|
capheight_(capheight),
|
||||||
|
bpp_(bpp) {
|
||||||
glyphs_.reserve(data_nr);
|
glyphs_.reserve(data_nr);
|
||||||
for (int i = 0; i < data_nr; ++i)
|
for (int i = 0; i < data_nr; ++i)
|
||||||
glyphs_.emplace_back(&data[i]);
|
glyphs_.emplace_back(&data[i]);
|
||||||
|
@ -50,11 +50,17 @@ class Font
|
|||||||
public:
|
public:
|
||||||
/** Construct the font with the given glyphs.
|
/** Construct the font with the given glyphs.
|
||||||
*
|
*
|
||||||
* @param glyphs A vector of glyphs, must be sorted lexicographically.
|
* @param data A vector of glyphs, must be sorted lexicographically.
|
||||||
|
* @param data_nr The number of glyphs in data.
|
||||||
* @param baseline The y-offset from the top of the text to the baseline.
|
* @param baseline The y-offset from the top of the text to the baseline.
|
||||||
* @param bottom The y-offset from the top of the text to the bottom (i.e. height).
|
* @param height The y-offset from the top of the text to the bottom.
|
||||||
|
* @param descender The y-offset from the baseline to the lowest stroke in the font (e.g. from letters like g or p).
|
||||||
|
* @param xheight The height of lowercase letters, usually measured at the "x" glyph.
|
||||||
|
* @param capheight The height of capital letters, usually measured at the "X" glyph.
|
||||||
|
* @param bpp The bits per pixel used for this font. Used to read data out of the glyph bitmaps.
|
||||||
*/
|
*/
|
||||||
Font(const GlyphData *data, int data_nr, int baseline, int height, uint8_t bpp = 1);
|
Font(const GlyphData *data, int data_nr, int baseline, int height, int descender, int xheight, int capheight,
|
||||||
|
uint8_t bpp = 1);
|
||||||
|
|
||||||
int match_next_glyph(const uint8_t *str, int *match_length);
|
int match_next_glyph(const uint8_t *str, int *match_length);
|
||||||
|
|
||||||
@ -65,6 +71,11 @@ class Font
|
|||||||
#endif
|
#endif
|
||||||
inline int get_baseline() { return this->baseline_; }
|
inline int get_baseline() { return this->baseline_; }
|
||||||
inline int get_height() { return this->height_; }
|
inline int get_height() { return this->height_; }
|
||||||
|
inline int get_ascender() { return this->baseline_; }
|
||||||
|
inline int get_descender() { return this->descender_; }
|
||||||
|
inline int get_linegap() { return this->linegap_; }
|
||||||
|
inline int get_xheight() { return this->xheight_; }
|
||||||
|
inline int get_capheight() { return this->capheight_; }
|
||||||
inline int get_bpp() { return this->bpp_; }
|
inline int get_bpp() { return this->bpp_; }
|
||||||
|
|
||||||
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
||||||
@ -73,6 +84,10 @@ class Font
|
|||||||
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
|
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
|
||||||
int baseline_;
|
int baseline_;
|
||||||
int height_;
|
int height_;
|
||||||
|
int descender_;
|
||||||
|
int linegap_;
|
||||||
|
int xheight_;
|
||||||
|
int capheight_;
|
||||||
uint8_t bpp_; // bits per pixel
|
uint8_t bpp_; // bits per pixel
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user