Add mem free printing (I need it to verify a suspicion relating to a bug)

This commit is contained in:
2024-04-22 13:28:24 -05:00
parent 20cd951cde
commit c60251359c
120 changed files with 31903 additions and 2 deletions

17
node_modules/lv_font_conv/lib/writers/bin.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// Write font in binary format
'use strict';
const AppError = require('../app_error');
const Font = require('../font/font');
module.exports = function write_images(args, fontData) {
if (!args.output) throw new AppError('Output is required for "bin" writer');
const font = new Font(fontData, args);
return {
[args.output]: font.toBin()
};
};

68
node_modules/lv_font_conv/lib/writers/dump.js generated vendored Normal file
View File

@@ -0,0 +1,68 @@
// Write font data into png images
'use strict';
const path = require('path');
const { PNG } = require('pngjs');
const AppError = require('../app_error');
const utils = require('../utils');
const normal_color = [ 255, 255, 255 ];
const outside_color = [ 255, 127, 184 ];
module.exports = function write_images(args, font) {
if (!args.output) throw new AppError('Output is required for "dump" writer');
let files = {};
let glyphs = font.glyphs.map(glyph => utils.set_depth(glyph, args.bpp));
for (let glyph of glyphs) {
let { code, advanceWidth, bbox, pixels } = glyph;
advanceWidth = Math.round(advanceWidth);
let minX = bbox.x;
let maxX = Math.max(bbox.x + bbox.width - 1, bbox.x);
let minY = Math.min(bbox.y, font.typoDescent);
let maxY = Math.max(bbox.y + bbox.height - 1, font.typoAscent);
let png = new PNG({ width: maxX - minX + 1, height: maxY - minY + 1 });
/* eslint-disable max-depth */
for (let pos = 0, y = maxY; y >= minY; y--) {
for (let x = minX; x <= maxX; x++) {
let value = 0;
if (x >= bbox.x && x < bbox.x + bbox.width && y >= bbox.y && y < bbox.y + bbox.height) {
value = pixels[bbox.height - (y - bbox.y) - 1][x - bbox.x];
}
let r, g, b;
if (x < 0 || x >= advanceWidth || y < font.typoDescent || y > font.typoAscent) {
[ r, g, b ] = outside_color;
} else {
[ r, g, b ] = normal_color;
}
png.data[pos++] = (255 - value) * r / 255;
png.data[pos++] = (255 - value) * g / 255;
png.data[pos++] = (255 - value) * b / 255;
png.data[pos++] = 255;
}
}
files[path.join(args.output, `${code.toString(16)}.png`)] = PNG.sync.write(png);
}
files[path.join(args.output, 'font_info.json')] = JSON.stringify(
font,
(k, v) => (k === 'pixels' && !args.full_info ? undefined : v),
2);
return files;
};

17
node_modules/lv_font_conv/lib/writers/lvgl/index.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
// Write font in lvgl format
'use strict';
const AppError = require('../../app_error');
const Font = require('./lv_font');
module.exports = function write_images(args, fontData) {
if (!args.output) throw new AppError('Output is required for "lvgl" writer');
const font = new Font(fontData, args);
return {
[args.output]: font.toLVGL()
};
};

98
node_modules/lv_font_conv/lib/writers/lvgl/lv_font.js generated vendored Normal file
View File

@@ -0,0 +1,98 @@
'use strict';
const path = require('path');
const Font = require('../../font/font');
const Head = require('./lv_table_head');
const Cmap = require('./lv_table_cmap');
const Glyf = require('./lv_table_glyf');
const Kern = require('./lv_table_kern');
const AppError = require('../../app_error');
class LvFont extends Font {
constructor(fontData, options) {
super(fontData, options);
const ext = path.extname(options.output);
this.font_name = path.basename(options.output, ext);
if (options.bpp === 3 & options.no_compress) {
throw new AppError('LittlevGL supports "--bpp 3" with compression only');
}
}
init_tables() {
this.head = new Head(this);
this.glyf = new Glyf(this);
this.cmap = new Cmap(this);
this.kern = new Kern(this);
}
large_format_guard() {
let guard_required = false;
let glyphs_bin_size = 0;
this.glyf.lv_data.forEach(d => {
glyphs_bin_size += d.bin.length;
if (d.glyph.bbox.width > 255 ||
d.glyph.bbox.height > 255 ||
Math.abs(d.glyph.bbox.x) > 127 ||
Math.abs(d.glyph.bbox.y) > 127 ||
Math.round(d.glyph.advanceWidth * 16) > 4096) {
guard_required = true;
}
});
if (glyphs_bin_size > 1024 * 1024) guard_required = true;
if (!guard_required) return '';
return `
#if (LV_FONT_FMT_TXT_LARGE == 0)
# error "Too large font or glyphs in ${this.font_name.toUpperCase()}. Enable LV_FONT_FMT_TXT_LARGE in lv_conf.h")
#endif
`.trimLeft();
}
toLVGL() {
let guard_name = this.font_name.toUpperCase();
return `/*******************************************************************************
* Size: ${this.src.size} px
* Bpp: ${this.opts.bpp}
* Opts: ${process.argv.slice(2).join(' ')}
******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "${this.opts.lv_include || 'lvgl/lvgl.h'}"
#endif
#ifndef ${guard_name}
#define ${guard_name} 1
#endif
#if ${guard_name}
${this.glyf.toLVGL()}
${this.cmap.toLVGL()}
${this.kern.toLVGL()}
${this.head.toLVGL()}
${this.large_format_guard()}
#endif /*#if ${guard_name}*/
`;
}
}
module.exports = LvFont;

View File

@@ -0,0 +1,125 @@
'use strict';
const u = require('../../utils');
const build_subtables = require('../../font/cmap_build_subtables');
const Cmap = require('../../font/table_cmap');
class LvCmap extends Cmap {
constructor(font) {
super(font);
this.lv_compiled = false;
this.lv_subtables = [];
}
lv_format2enum(name) {
switch (name) {
case 'format0_tiny': return 'LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY';
case 'format0': return 'LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL';
case 'sparse_tiny': return 'LV_FONT_FMT_TXT_CMAP_SPARSE_TINY';
case 'sparse': return 'LV_FONT_FMT_TXT_CMAP_SPARSE_FULL';
default: throw new Error('Unknown subtable format');
}
}
lv_compile() {
if (this.lv_compiled) return;
this.lv_compiled = true;
const f = this.font;
let subtables_plan = build_subtables(f.src.glyphs.map(g => g.code));
let idx = 0;
for (let [ format, codepoints ] of subtables_plan) {
let g = this.glyphByCode(codepoints[0]);
let start_glyph_id = f.glyph_id[g.code];
let min_code = codepoints[0];
let max_code = codepoints[codepoints.length - 1];
let has_charcodes = false;
let has_ids = false;
let defs = '';
let entries_count = 0;
if (format === 'format0_tiny') {
// use default empty values
} else if (format === 'format0') {
has_ids = true;
let d = this.collect_format0_data(min_code, max_code, start_glyph_id);
entries_count = d.length;
defs = `
static const uint8_t glyph_id_ofs_list_${idx}[] = {
${u.long_dump(d)}
};
`.trim();
} else if (format === 'sparse_tiny') {
has_charcodes = true;
let d = this.collect_sparse_data(codepoints, start_glyph_id);
entries_count = d.codes.length;
defs = `
static const uint16_t unicode_list_${idx}[] = {
${u.long_dump(d.codes, { hex: true })}
};
`.trim();
} else { // assume format === 'sparse'
has_charcodes = true;
has_ids = true;
let d = this.collect_sparse_data(codepoints, start_glyph_id);
entries_count = d.codes.length;
defs = `
static const uint16_t unicode_list_${idx}[] = {
${u.long_dump(d.codes, { hex: true })}
};
static const uint16_t glyph_id_ofs_list_${idx}[] = {
${u.long_dump(d.ids)}
};
`.trim();
}
const u_list = has_charcodes ? `unicode_list_${idx}` : 'NULL';
const id_list = has_ids ? `glyph_id_ofs_list_${idx}` : 'NULL';
/* eslint-disable max-len */
const head = ` {
.range_start = ${min_code}, .range_length = ${max_code - min_code + 1}, .glyph_id_start = ${start_glyph_id},
.unicode_list = ${u_list}, .glyph_id_ofs_list = ${id_list}, .list_length = ${entries_count}, .type = ${this.lv_format2enum(format)}
}`;
this.lv_subtables.push({
defs,
head
});
idx++;
}
}
toLVGL() {
this.lv_compile();
return `
/*---------------------
* CHARACTER MAPPING
*--------------------*/
${this.lv_subtables.map(d => d.defs).filter(Boolean).join('\n\n')}
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] =
{
${this.lv_subtables.map(d => d.head).join(',\n')}
};
`.trim();
}
}
module.exports = LvCmap;

View File

@@ -0,0 +1,121 @@
'use strict';
const { BitStream } = require('bit-buffer');
const u = require('../../utils');
const Glyf = require('../../font/table_glyf');
class LvGlyf extends Glyf {
constructor(font) {
super(font);
this.lv_data = [];
this.lv_compiled = false;
}
lv_bitmap(glyph) {
const buf = Buffer.alloc(100 + glyph.bbox.width * glyph.bbox.height * 4);
const bs = new BitStream(buf);
bs.bigEndian = true;
const pixels = this.font.glyf.pixelsToBpp(glyph.pixels);
this.font.glyf.storePixels(bs, pixels);
const glyph_bitmap = Buffer.alloc(bs.byteIndex);
buf.copy(glyph_bitmap, 0, 0, bs.byteIndex);
return glyph_bitmap;
}
lv_compile() {
if (this.lv_compiled) return;
this.lv_compiled = true;
const f = this.font;
this.lv_data = [];
let offset = 0;
f.src.glyphs.forEach(g => {
const id = f.glyph_id[g.code];
const bin = this.lv_bitmap(g);
this.lv_data[id] = {
bin,
offset,
glyph: g
};
offset += bin.length;
});
}
to_lv_bitmaps() {
this.lv_compile();
let result = [];
this.lv_data.forEach((d, idx) => {
if (idx === 0) return;
const code_hex = d.glyph.code.toString(16).toUpperCase();
const code_str = JSON.stringify(String.fromCodePoint(d.glyph.code));
let txt = ` /* U+${code_hex.padStart(4, '0')} ${code_str} */
${u.long_dump(d.bin, { hex: true })}`;
if (idx < this.lv_data.length - 1) {
// skip comma for zero data
txt += d.bin.length ? ',\n\n' : '\n';
}
result.push(txt);
});
return result.join('');
}
to_lv_glyph_dsc() {
this.lv_compile();
/* eslint-disable max-len */
let result = [ ' {.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */' ];
this.lv_data.forEach(d => {
const idx = d.offset,
adv_w = Math.round(d.glyph.advanceWidth * 16),
h = d.glyph.bbox.height,
w = d.glyph.bbox.width,
x = d.glyph.bbox.x,
y = d.glyph.bbox.y;
result.push(` {.bitmap_index = ${idx}, .adv_w = ${adv_w}, .box_w = ${w}, .box_h = ${h}, .ofs_x = ${x}, .ofs_y = ${y}}`);
});
return result.join(',\n');
}
toLVGL() {
return `
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
${this.to_lv_bitmaps()}
};
/*---------------------
* GLYPH DESCRIPTION
*--------------------*/
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
${this.to_lv_glyph_dsc()}
};
`.trim();
}
}
module.exports = LvGlyf;

View File

@@ -0,0 +1,99 @@
'use strict';
const Head = require('../../font/table_head');
class LvHead extends Head {
constructor(font) {
super(font);
}
kern_ref() {
const f = this.font;
if (!f.hasKerning()) {
return {
scale: '0',
dsc: 'NULL',
classes: '0'
};
}
if (!f.kern.should_use_format3()) {
return {
scale: `${Math.round(f.kerningScale * 16)}`,
dsc: '&kern_pairs',
classes: '0'
};
}
return {
scale: `${Math.round(f.kerningScale * 16)}`,
dsc: '&kern_classes',
classes: '1'
};
}
toLVGL() {
const f = this.font;
const kern = this.kern_ref();
const subpixels = (f.subpixels_mode === 0) ? 'LV_FONT_SUBPX_NONE' :
(f.subpixels_mode === 1) ? 'LV_FONT_SUBPX_HOR' : 'LV_FONT_SUBPX_VER';
return `
/*--------------------
* ALL CUSTOM DATA
*--------------------*/
#if LV_VERSION_CHECK(8, 0, 0)
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
static const lv_font_fmt_txt_dsc_t font_dsc = {
#else
static lv_font_fmt_txt_dsc_t font_dsc = {
#endif
.glyph_bitmap = glyph_bitmap,
.glyph_dsc = glyph_dsc,
.cmaps = cmaps,
.kern_dsc = ${kern.dsc},
.kern_scale = ${kern.scale},
.cmap_num = ${f.cmap.toBin().readUInt32LE(8)},
.bpp = ${f.opts.bpp},
.kern_classes = ${kern.classes},
.bitmap_format = ${f.glyf.getCompressionCode()},
#if LV_VERSION_CHECK(8, 0, 0)
.cache = &cache
#endif
};
/*-----------------
* PUBLIC FONT
*----------------*/
/*Initialize a public general font descriptor*/
#if LV_VERSION_CHECK(8, 0, 0)
const lv_font_t ${f.font_name} = {
#else
lv_font_t ${f.font_name} = {
#endif
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
.line_height = ${f.src.ascent - f.src.descent}, /*The maximum line height required by the font*/
.base_line = ${-f.src.descent}, /*Baseline measured from the bottom of the line*/
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
.subpx = ${subpixels},
#endif
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
.underline_position = ${f.src.underlinePosition},
.underline_thickness = ${f.src.underlineThickness},
#endif
.dsc = &font_dsc /*The custom font data. Will be accessed by \`get_glyph_bitmap/dsc\` */
};
`.trim();
}
}
module.exports = LvHead;

View File

@@ -0,0 +1,121 @@
'use strict';
const u = require('../../utils');
const Kern = require('../../font/table_kern');
class LvKern extends Kern {
constructor(font) {
super(font);
}
to_lv_format0() {
const f = this.font;
let kern_pairs = this.collect_format0_data();
return `
/*-----------------
* KERNING
*----------------*/
/*Pair left and right glyphs for kerning*/
static const ${f.glyphIdFormat ? 'uint16_t' : 'uint8_t'} kern_pair_glyph_ids[] =
{
${kern_pairs.map(pair => ` ${pair[0]}, ${pair[1]}`).join(',\n')}
};
/* Kerning between the respective left and right glyphs
* 4.4 format which needs to scaled with \`kern_scale\`*/
static const int8_t kern_pair_values[] =
{
${u.long_dump(kern_pairs.map(pair => f.kernToFP(pair[2])))}
};
/*Collect the kern pair's data in one place*/
static const lv_font_fmt_txt_kern_pair_t kern_pairs =
{
.glyph_ids = kern_pair_glyph_ids,
.values = kern_pair_values,
.pair_cnt = ${kern_pairs.length},
.glyph_ids_size = ${f.glyphIdFormat}
};
`.trim();
}
to_lv_format3() {
const f = this.font;
const {
left_classes,
right_classes,
left_mapping,
right_mapping,
values
} = this.collect_format3_data();
return `
/*-----------------
* KERNING
*----------------*/
/*Map glyph_ids to kern left classes*/
static const uint8_t kern_left_class_mapping[] =
{
${u.long_dump(left_mapping)}
};
/*Map glyph_ids to kern right classes*/
static const uint8_t kern_right_class_mapping[] =
{
${u.long_dump(right_mapping)}
};
/*Kern values between classes*/
static const int8_t kern_class_values[] =
{
${u.long_dump(values.map(v => f.kernToFP(v)))}
};
/*Collect the kern class' data in one place*/
static const lv_font_fmt_txt_kern_classes_t kern_classes =
{
.class_pair_values = kern_class_values,
.left_class_mapping = kern_left_class_mapping,
.right_class_mapping = kern_right_class_mapping,
.left_class_cnt = ${left_classes},
.right_class_cnt = ${right_classes},
};
`.trim();
}
toLVGL() {
const f = this.font;
if (!f.hasKerning()) return '';
/* eslint-disable no-console */
if (f.kern.should_use_format3()) {
if (f.kern.format3_forced) {
let diff = this.create_format3_data().length - this.create_format0_data().length;
console.log(`Forced faster kerning format (via classes). Size increase is ${diff} bytes.`);
}
return this.to_lv_format3();
}
if (this.font.opts.fast_kerning) {
console.log('Forced faster kerning format (via classes), but data exceeds it\'s limits. Continue use pairs.');
}
return this.to_lv_format0();
}
}
module.exports = LvKern;