Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,528 @@
/*
* lws abstract display
*
* Copyright (C) 2013 Petteri Aimonen
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: mcufont font
*
* The mcu decoding is rewritten from the mcufont implementation at
* https://github.com/mcufont/mcufont, which is licensed under MIT already,
* to use a stateful decoder.
*
* The decoder only brings in new compression codes when needed to produce more
* pixels on the line of the glyphs being decoded.
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
#define DICT_START 24
#define REF_FILLZEROS 16
#define RLE_CODEMASK 0xC0
#define RLE_VALMASK 0x3F
#define RLE_ZEROS 0x00
#define RLE_64ZEROS 0x40
#define RLE_ONES 0x80
#define RLE_SHADE 0xC0
#define DICT_START7BIT 4
#define DICT_START6BIT 132
#define DICT_START5BIT 196
#define DICT_START4BIT 228
#define DICT_START3BIT 244
#define DICT_START2BIT 252
enum {
RS_IDLE,
RS_SKIP_PX,
RS_WRITE_PX,
RS_ALLZERO,
COMP = 0,
DICT1,
DICT1_CONT,
DICT2,
DICT3
};
typedef struct mcu_stack {
const uint8_t *dict;
int16_t dictlen;
int16_t runlen; /* for accumilation on DICT1 */
uint8_t byte;
uint8_t bitcount;
uint8_t state;
} mcu_stack_t;
typedef struct mcu_glyph {
lws_font_glyph_t fg;
const uint8_t *comp;
mcu_stack_t st[3];
int32_t runlen;
int8_t sp;
uint8_t runstate;
uint8_t alpha;
uint8_t code;
} mcu_glyph_t;
/* Get bit count for the "fill entries" */
static uint8_t
fillentry_bitcount(uint8_t index)
{
if (index >= DICT_START2BIT)
return 2;
else if (index >= DICT_START3BIT)
return 3;
else if (index >= DICT_START4BIT)
return 4;
else if (index >= DICT_START5BIT)
return 5;
else if (index >= DICT_START6BIT)
return 6;
else
return 7;
}
void
draw_px(lws_dlo_text_t *t, mcu_glyph_t *g)
{
lws_display_colour_t c = (lws_display_colour_t)((lws_display_colour_t)(g->alpha << 24) |
(lws_display_colour_t)((lws_display_colour_t)t->dlo.dc & 0xffffffu));
lws_fx_t t1, x;
int ex;
t1.whole = g->fg.x;
if (!g->alpha)
return;
t1.frac = 0;
lws_fx_add(&x, &g->fg.xpx, &t1);
#if 0
{ char b1[22], b2[22], b3[22];
lwsl_err("fadj %s = %s + %s\n",
lws_fx_string(&x, b1, sizeof(b1)),
lws_fx_string(&g->fg.xpx, b2, sizeof(b2)),
lws_fx_string(&g->fg.xorg, b3, sizeof(b3))); }
#endif
ex = x.whole;// - t->dlo.box.x.whole;
if (ex < 0 || ex >= t->dlo.box.w.whole) {
//lwsl_err("%s: ex %d (lim %d)\n", __func__, ex, t->dlo.box.w.whole);
return;
}
lws_fx_add(&x, &x, &g->fg.xorg);
lws_fx_add(&t1, &t->dlo.box.x, &x);
lws_surface_set_px(t->ic, t->line, t1.whole, &c);
}
static void
write_ref_codeword(mcu_glyph_t *g, const uint8_t *bf, uint8_t c)
{
uint32_t o, o1;
if (!c) {
g->runlen = 1;
g->runstate = RS_SKIP_PX;
return;
}
if (c <= 15) {
g->alpha = (uint8_t)(0x11 * c);
g->runlen = 1;
g->runstate = RS_WRITE_PX;
return;
}
if (c == REF_FILLZEROS) {
/* Fill with zeroes to end */
g->alpha = 0;
g->runlen = 1000000;
g->runstate = RS_WRITE_PX;
return;
}
if (c < DICT_START)
return;
if (c < DICT_START + lws_ser_ru32be(bf + MCUFO_COUNT_RLE_DICT)) {
/* write_rle_dictentry */
o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS);
o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2));
g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 +
((c - DICT_START + 1) * 2)) - o);
g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o;
g->st[(int)g->sp].state = DICT2;
return;
}
g->st[(int)++g->sp].bitcount = fillentry_bitcount(c);
g->st[(int)g->sp].byte = (uint8_t)(c - DICT_START7BIT);
g->st[(int)g->sp].state = DICT1;
g->runlen = 0;
}
static void
mcufont_next_code(mcu_glyph_t *g)
{
lws_dlo_text_t *t = lws_container_of(g->fg.list.owner, lws_dlo_text_t,
glyphs);
const uint8_t *bf = (const uint8_t *)t->font->data;
uint8_t c = *g->comp++;
uint32_t o, o1;
if (c < DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_RLE_DICT]) ||
c >= DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_REF_RLE_DICT])) {
write_ref_codeword(g, bf, c);
return;
}
/* write_ref_dictentry() */
o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS);
o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2));
g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 +
((c - DICT_START + 1) * 2)) - o);
g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o;
g->st[(int)g->sp].state = DICT3;
}
/* lookup and append a glyph for specific unicode to the text glyph list */
static uint32_t
font_mcufont_uniglyph_lookup(lws_dlo_text_t *text, uint32_t unicode)
{
const uint8_t *bf = (const uint8_t *)text->font->data,
*r = bf + lws_ser_ru32be(&bf[MCUFO_FOFS_CHAR_RANGE_TABLES]);
uint32_t entries = lws_ser_ru32be(&bf[MCUFO_COUNT_CHAR_RANGE_TABLES]);
unsigned int n;
if (entries > 8) /* coverity sanity */
return 0;
do {
for (n = 0; n < entries; n++) {
uint32_t cs = lws_ser_ru32be(r + 0), ce = lws_ser_ru32be(r + 4);
if (cs >= 0x100000 || !ce || ce > 0x10000)
return 0;
if (unicode >= cs && unicode < cs + ce) {
uint32_t cbo = lws_ser_ru32be(r + 0xc);
if (cbo >= text->font->data_len)
return 0;
cbo += lws_ser_ru16be(bf +
lws_ser_ru32be(r + 8) + ((unicode - cs) * 2));
if (cbo >= text->font->data_len)
return 0;
return cbo;
}
r += 16;
}
if (unicode == lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK]))
return 0;
unicode = lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK]);
} while (1);
}
static mcu_glyph_t *
font_mcufont_uniglyph(lws_dlo_text_t *text, uint32_t unicode)
{
const uint8_t *bf = (const uint8_t *)text->font->data;
uint32_t ofs;
mcu_glyph_t *g;
ofs = font_mcufont_uniglyph_lookup(text, unicode);
if (!ofs)
return NULL;
// lwsl_warn("%s: text->text_len %u: %c\n", __func__, text->text_len, (char)unicode);
g = lwsac_use_zero(&text->ac_glyphs, sizeof(*g),
(text->text_len + 1) * sizeof(*g));
if (!g)
return NULL;
g->comp = bf + ofs;
g->fg.cwidth.whole = *g->comp++;
g->fg.cwidth.frac = 0;
lws_dll2_add_tail(&g->fg.list, &text->glyphs);
return g;
}
int
lws_display_font_mcufont_getcwidth(lws_dlo_text_t *text, uint32_t unicode,
lws_fx_t *fx)
{
const uint8_t *bf = (const uint8_t *)text->font->data;
uint32_t ofs = font_mcufont_uniglyph_lookup(text, unicode);
if (!ofs)
return 1;
fx->whole = bf[ofs];
fx->frac = 0;
return 0;
}
lws_font_glyph_t *
lws_display_font_mcufont_image_glyph(lws_dlo_text_t *text, uint32_t unicode,
char attach)
{
const uint8_t *bf = (const uint8_t *)text->font->data;
mcu_glyph_t *g;
/* one text dlo has glyphs from all the same fonts and attributes */
if (!text->font_height) {
text->font_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_HEIGHT]);
text->font_y_baseline = (int16_t)(text->font_height -
lws_ser_ru16be(&bf[MCUFO16_BASELINE_Y]));
text->font_line_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_LINE_HEIGHT]);
}
lws_display_font_mcufont_getcwidth(text, unicode, &text->_cwidth);
if (!attach)
return NULL;
g = font_mcufont_uniglyph(text, unicode);
if (!g)
return NULL;
g->fg.height.whole = lws_ser_ru16be(bf + MCUFO16_HEIGHT);
g->fg.height.frac = 0;
return &g->fg;
}
lws_stateful_ret_t
lws_display_font_mcufont_render(struct lws_display_render_state *rs)
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
const uint8_t *bf = (const uint8_t *)text->font->data;
lws_fx_t ax, ay, t, t1, t2, t3;
mcu_glyph_t *g;
int s, e, yo;
uint8_t c, el;
lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&t, &ax, &dlo->box.w);
lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&t1, &ay, &dlo->box.h);
lws_fx_add(&t2, &ax, &text->bounding_box.w);
text->curr = rs->curr;
text->ic = rs->ic;
text->line = rs->line;
s = ax.whole;
e = lws_fx_roundup(&t2);
if (e <= 0)
return LWS_SRET_OK; /* wholly off to the left */
if (s >= rs->ic->wh_px[0].whole)
return LWS_SRET_OK; /* wholly off to the right */
if (e >= rs->ic->wh_px[0].whole)
e = rs->ic->wh_px[0].whole;
/* figure out our y position inside the glyph bounding box */
yo = rs->curr - ay.whole;
if (!yo) {
lws_display_dlo_text_attach_glyphs(text);
t3.whole = lws_ser_ru16be(bf + MCUFO16_BASELINE_X);
t3.frac = 0;
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&text->glyphs)) {
lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list);
lws_fx_sub(&fg->xpx, &fg->xpx, &t3);
fg->xorg = rs->st[rs->sp].co.x;
} lws_end_foreach_dll(d);
}
#if 0
{
uint32_t dc = 0xff0000ff;
int s1 = s;
/* from origin.x + dlo->box.x */
for (s1 = ax.whole; s1 < t2.whole; s1++)
lws_surface_set_px(ic, line, s1, &dc);
memset(&ce, 0, sizeof(ce));
}
#endif
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&text->glyphs)) {
lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list);
g = (mcu_glyph_t *)fg;
fg->x = 0;
while (yo < (int)fg->height.whole &&
fg->x < lws_ser_ru16be(bf + MCUFO16_WIDTH)) {
switch (g->runstate) {
case RS_IDLE:
switch (g->st[(int)g->sp].state) {
case COMP:
mcufont_next_code(g);
break;
case DICT1_CONT:
--g->sp; /* back to DICT1 after doing the skip */
g->runstate = RS_SKIP_PX;
g->runlen = 1;
continue;
case DICT1:
/* write_bin_codeword() states */
el = 0;
while (g->st[(int)g->sp].bitcount--) {
c = g->st[(int)g->sp].byte;
g->st[(int)g->sp].byte >>= 1;
if (c & 1)
g->st[(int)g->sp].runlen++;
else {
if (g->st[(int)g->sp].runlen) {
g->alpha = 255;
g->runstate = RS_WRITE_PX;
g->runlen = g->st[(int)g->sp].runlen;
g->st[(int)g->sp].runlen = 0;
g->st[(int)++g->sp].state = DICT1_CONT;
el = 1;
break;
}
g->runstate = RS_SKIP_PX;
g->runlen = 1;
el = 1;
break;
}
}
if (el)
continue;
/* back out of DICT1 */
if (!g->sp)
assert(0);
g->sp--;
if (g->st[(int)g->sp + 1].runlen) {
g->alpha = 255;
g->runstate = RS_WRITE_PX;
g->runlen = g->st[(int)g->sp + 1].runlen;
g->st[(int)g->sp + 1].runlen = 0;
continue;
}
break;
case DICT2: /* write_rle_dictentry */
c = (*g->st[(int)g->sp].dict++);
if (!--g->st[(int)g->sp].dictlen) {
if (!g->sp)
assert(0);
g->sp--;
}
if ((c & RLE_CODEMASK) == RLE_ZEROS) {
g->runstate = RS_SKIP_PX;
g->runlen = c & RLE_VALMASK;
continue;
}
if ((c & RLE_CODEMASK) == RLE_64ZEROS) {
g->runstate = RS_SKIP_PX;
g->runlen = ((c & RLE_VALMASK) + 1) * 64;
continue;
}
if ((c & RLE_CODEMASK) == RLE_ONES) {
g->alpha = 255;
g->runstate = RS_WRITE_PX;
g->runlen = (c & RLE_VALMASK) + 1;
continue;
}
if ((c & RLE_CODEMASK) == RLE_SHADE) {
g->alpha = (uint8_t)(((c & RLE_VALMASK) & 0xf) * 0x11);
g->runstate = RS_WRITE_PX;
g->runlen = ((c & RLE_VALMASK) >> 4) + 1;
continue;
}
break;
case DICT3:
c = *g->st[(int)g->sp].dict++;
if (!--g->st[(int)g->sp].dictlen) {
if (!g->sp)
assert(0);
g->sp--;
}
write_ref_codeword(g, bf, c);
break;
}
break;
case RS_SKIP_PX:
fg->x++;
if (--g->runlen)
break;
g->runstate = RS_IDLE;
break;
case RS_WRITE_PX:
if (g->alpha)
draw_px(text, g);
g->fg.x++;
if (--g->runlen)
break;
g->runstate = RS_IDLE;
break;
case RS_ALLZERO:
fg->x++;
if (--g->runlen)
break;
g->runstate = RS_IDLE;
break;
}
}
} lws_end_foreach_dll(d);
return LWS_SRET_OK;
}

View File

@ -0,0 +1,195 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: JPEG
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
void
lws_display_dlo_jpeg_destroy(struct lws_dlo *dlo)
{
lws_dlo_jpeg_t *dlo_jpeg = lws_container_of(dlo, lws_dlo_jpeg_t, dlo);
#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS)
lws_ss_destroy(&dlo_jpeg->flow.h);
#endif
lws_buflist_destroy_all_segments(&dlo_jpeg->flow.bl);
if (dlo_jpeg->j)
lws_jpeg_free(&dlo_jpeg->j);
}
lws_stateful_ret_t
lws_display_render_jpeg(struct lws_display_render_state *rs)
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_jpeg_t *dlo_jpeg = lws_container_of(dlo, lws_dlo_jpeg_t, dlo);
lws_display_colour_t pc;
lws_fx_t ax, ay, t, t1;
lws_stateful_ret_t r;
const uint8_t *pix;
int s, e;
lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&t, &ax, &dlo->box.w);
lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&t1, &ay, &dlo->box.h);
if (!lws_jpeg_get_height(dlo_jpeg->j)) {
lwsl_info("%s: jpeg does not have dimensions yet\n", __func__);
return LWS_SRET_WANT_INPUT;
}
s = ax.whole;
e = lws_fx_roundup(&t);
if (rs->curr > lws_fx_roundup(&t1))
return LWS_SRET_OK;
if (rs->curr - lws_fx_roundup(&ay) >
(int)lws_jpeg_get_height(dlo_jpeg->j))
return LWS_SRET_OK;
if (s < 0)
s = 0;
if (s > rs->ic->wh_px[0].whole)
return LWS_SRET_OK; /* off to the right */
if (e > rs->ic->wh_px[0].whole)
e = rs->ic->wh_px[0].whole - 1;
if (e <= 0)
return LWS_SRET_OK; /* off to the left */
do {
if (lws_flow_feed(&dlo_jpeg->flow))
/* if he says WANT_INPUT, we have nothing in the buflist */
return LWS_SRET_WANT_INPUT;
pix = NULL;
r = lws_jpeg_emit_next_line(dlo_jpeg->j, &pix, &dlo_jpeg->flow.data,
&dlo_jpeg->flow.len, rs->html == 1);
if (r & LWS_SRET_NO_FURTHER_IN)
dlo_jpeg->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
if (r & LWS_SRET_FATAL || r == LWS_SRET_OK)
return r;
r = lws_flow_req(&dlo_jpeg->flow);
if (r & LWS_SRET_WANT_INPUT)
return r;
} while (!pix);
/*
* What's in pix is either 24-bit RGB 3 bytes/px, or 8-bit grayscale
* 1 byte/px, we have to map it on to either 32-bit RGBA or 16-bit YA
* composition buf
*/
pix = pix + (( (unsigned int)(s - ax.whole) *
(lws_jpeg_get_pixelsize(dlo_jpeg->j) / 8)));
while (s < e && s >= ax.whole && s < lws_fx_roundup(&t) &&
(s - ax.whole) < (int)lws_jpeg_get_width(dlo_jpeg->j)) {
if (lws_jpeg_get_pixelsize(dlo_jpeg->j) == 8)
pc = LWSDC_RGBA(pix[0], pix[0], pix[0], 255);
else
pc = LWSDC_RGBA(pix[0], pix[1], pix[2], 255);
lws_surface_set_px(rs->ic, rs->line, s, &pc);
s++;
pix += lws_jpeg_get_pixelsize(dlo_jpeg->j) / 8;
}
return LWS_SRET_OK;
}
lws_stateful_ret_t
lws_display_dlo_jpeg_metadata_scan(lws_dlo_jpeg_t *dlo_jpeg)
{
lws_stateful_ret_t r;
size_t l, l1;
const uint8_t *pix;
/*
* If we don't have the image metadata yet, provide small chunks of the
* source data until we do have the image metadata, but small enough
* we can't produce any decoded pixels too early.
*/
while (!lws_jpeg_get_height(dlo_jpeg->j) && dlo_jpeg->flow.len) {
l1 = l = dlo_jpeg->flow.len > 128 ? 128 : dlo_jpeg->flow.len;
r = lws_jpeg_emit_next_line(dlo_jpeg->j, &pix, &dlo_jpeg->flow.data, &l, 1);
if (r >= LWS_SRET_FATAL) {
lwsl_err("%s: hdr parse failed %d\n", __func__, r);
return r;
}
dlo_jpeg->flow.len -= l1 - l;
if (lws_jpeg_get_height(dlo_jpeg->j)) {
lwsl_info("jpeg: w %d, h %d\n",
lws_jpeg_get_width(dlo_jpeg->j),
lws_jpeg_get_height(dlo_jpeg->j));
return LWS_SRET_OK;
}
}
return LWS_SRET_WANT_INPUT;
}
lws_dlo_jpeg_t *
lws_display_dlo_jpeg_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box)
{
lws_dlo_jpeg_t *dlo_jpeg = lws_zalloc(sizeof(*dlo_jpeg), __func__);
if (!dlo_jpeg)
return NULL;
dlo_jpeg->j = lws_jpeg_new();
if (!dlo_jpeg->j)
goto bail;
dlo_jpeg->dlo.box = *box;
dlo_jpeg->dlo.render = lws_display_render_jpeg;
dlo_jpeg->dlo._destroy = lws_display_dlo_jpeg_destroy;
lws_display_dlo_add(dl, dlo_parent, &dlo_jpeg->dlo);
return dlo_jpeg;
bail:
lwsl_err("%s: bailed\n", __func__);
if (dlo_jpeg->j)
lws_jpeg_free(&dlo_jpeg->j);
lws_free(dlo_jpeg);
return NULL;
}

View File

@ -0,0 +1,982 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List LHP layout
*
* The basic flow is logical elements exist in a stack as they are parsed, the
* job of lhp_displaylist_layout() is to translate these into a tree of DLOs,
* having parent-child relationships with (x,y) of the DLO box being an offset
* into a local origin formed from the DLO parent box (which in turn may be
* a child with its origin defined by its parent, etc).
*
* The element stack only exists while it and its parent elements are being
* parsed, it goes out of scope as the element ends. So we must create related
* DLOs by stream-parsing, while we still have everything relevant to hand.
*
* This gets us out of having to run around fixing up DLO (x,y) as we do the
* layout, since the DLO parent-child relationships are static even if their
* content size isn't.
*
*
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
/*
* HTML Elements we can deal with for layout
*/
enum {
/* 0 is no match */
LHP_ELEM_BR = 1,
LHP_ELEM_DIV,
LHP_ELEM_TABLE,
LHP_ELEM_TR,
LHP_ELEM_TD,
LHP_ELEM_IMG,
};
static const struct {
const char *elem;
uint8_t elem_len;
} elems[] = {
{ "br", 2 },
{ "div", 3 },
{ "table", 5 },
{ "tr", 2 },
{ "td", 2 },
{ "img", 3 },
};
/*
* Newline moves the psb->cury to cover text that was already placed using the
* old psb->cury as to top of it. So a final newline on the last line of text
* does not create an extra blank line.
*/
static const lws_fx_t two = { 2,0 };
static void
newline(lhp_ctx_t *ctx, lhp_pstack_t *psb, lhp_pstack_t *ps,
lws_displaylist_t *dl)
{
int16_t group_baseline = 9999, group_height = 0;
lws_fx_t line_height = { 0, 0 }, w, add, ew, t1;
const struct lcsp_atr *a;
lws_dlo_t *dlo, *d, *d1;
int t = 0;
if (!psb || !ps) {
lwsl_err("%s: psb/ps NULL!\n", __func__);
return;
}
dlo = (lws_dlo_t *)psb->dlo;
lws_fx_add(&w, lws_csp_px(ps->css_padding[CCPAS_LEFT], ps),
lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
if (lws_fx_comp(&w, &psb->widest) > 0)
psb->widest = w;
if (!dlo || !dlo->children.tail)
return;
d = lws_container_of(dlo->children.tail, lws_dlo_t, list);
/*
* We may be at the end of a line of text
*
* Figure out the biggest height on the line, and the total width
*/
while (d) {
t |= d->_destroy == lws_display_dlo_text_destroy;
/* find the "worst" height on the line */
if (lws_fx_comp(&d->box.h, &line_height) > 0)
line_height = d->box.h;
if (d->_destroy == lws_display_dlo_text_destroy) {
lws_dlo_text_t *text = lws_container_of(d,
lws_dlo_text_t, dlo.list);
if (text->font_y_baseline < group_baseline)
group_baseline = text->font_y_baseline;
if (text->font_height > group_height)
group_height = text->font_height;
}
if (!d->flag_runon)
break;
d = lws_container_of(d->list.prev, lws_dlo_t, list);
};
/* mark the related text dlos with information about group bl and h,
* offset box y to align to group baseline if necessary */
d1 = d;
while (d) {
if (d->_destroy == lws_display_dlo_text_destroy) {
lws_dlo_text_t *t1 = lws_container_of(d1,
lws_dlo_text_t, dlo.list);
lws_fx_t ft;
t1->group_height = group_height;
t1->group_y_baseline = group_baseline;
ft.whole = (t1->font_height - t1->font_y_baseline) -
(group_height - group_baseline);
ft.frac = 0;
lws_fx_sub(&t1->dlo.box.y, &t1->dlo.box.y, &ft);
}
if (!d1->list.next)
break;
d1 = lws_container_of(d1->list.next, lws_dlo_t, list);
};
w = psb->curx;
ew = ctx->ic.wh_px[0];
if (psb->css_width && psb->css_width->unit != LCSP_UNIT_NONE)
ew = *lws_csp_px(psb->css_width, psb);
lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
if (lws_fx_comp(&w, &psb->widest) > 0)
psb->widest = w;
if (!t) /* no textual children to newline (eg, <div></div>) */
return;
/*
* now is our chance to fix up dlos that are part of the line for
* text-align rule of the container.
*/
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN);
if (a) {
switch (a->propval) {
case LCSP_PROPVAL_CENTER:
add = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps);
lws_fx_sub(&t1, &ew, &w);
lws_fx_div(&t1, &t1, &two);
lws_fx_add(&add, &add, &t1);
goto fixup;
case LCSP_PROPVAL_RIGHT:
lws_fx_sub(&add, &ew, &w);
lws_fx_sub(&add, &add, &d->box.x);
fixup:
lws_fx_add(&t1, &add, &w);
if (lws_fx_comp(&t1, &psb->widest) > 0)
psb->widest = t1;
do {
lws_fx_add(&d->box.x, &d->box.x, &add);
if (!d->list.next)
break;
d = lws_container_of(d->list.next, lws_dlo_t,
list);
} while (1);
break;
default:
break;
}
}
lws_fx_add(&psb->cury, &psb->cury, &line_height);
lws_fx_set(psb->curx, 0, 0);
psb->dlo_set_curx = NULL;
psb->dlo_set_cury = NULL;
psb->runon = 0;
}
void
lhp_set_dlo_padding_margin(lhp_pstack_t *ps, lws_dlo_t *dlo)
{
int n;
for (n = 0; n < 4; n ++) {
if (ps->css_margin[n])
dlo->margin[n] = *lws_csp_px(ps->css_margin[n], ps);
else
lws_fx_set(dlo->margin[n], 0, 0);
if (ps->css_padding[n])
dlo->padding[n] = *lws_csp_px(ps->css_padding[n], ps);
else
lws_fx_set(dlo->padding[n], 0, 0);
}
}
void
lhp_set_dlo_adjust_to_contents(lhp_pstack_t *ps)
{
lhp_pstack_t *psb = lws_container_of(ps->list.prev, lhp_pstack_t, list);
lws_dlo_dim_t dim;
lws_dlo_contents(ps->dlo, &dim);
lws_display_dlo_adjust_dims(ps->dlo, &dim);
if (lws_fx_comp(&dim.w, &psb->widest) > 0)
psb->widest = dim.w;
if (lws_fx_comp(&dim.h, &psb->deepest) > 0)
psb->deepest = dim.h;
}
static void
runon(lhp_pstack_t *ps, lws_dlo_t *dlo)
{
dlo->flag_runon = (uint8_t)(ps->runon & 1);
ps->runon = 1;
}
/*
* Handle end-of-div, table, tr, td retrospective dlo dimension adjustment
*/
int
lws_lhp_dlo_adjust_div_type_element(lhp_ctx_t *ctx, lhp_pstack_t *psb,
lhp_pstack_t *pst, lhp_pstack_t *ps,
int elem_match)
{
lws_dlo_rect_t *rect = (lws_dlo_rect_t *)ps->dlo;
lws_fx_t t1, w, wd;
char rd = 0;
/* need this to get bottom clearance for next block */
lws_fx_add(&ps->cury, &ps->cury,
lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps));
if (psb && ps->dlo &&
ps->css_margin[CCPAS_LEFT]->propval == LCSP_PROPVAL_AUTO &&
ps->css_margin[CCPAS_RIGHT]->propval == LCSP_PROPVAL_AUTO) {
lws_dlo_rect_t *re = (lws_dlo_rect_t *)ps->dlo;
/* h-center a div... find the available h space first */
w = ctx->ic.wh_px[LWS_LHPREF_WIDTH];
if (psb->css_width &&
psb->css_width->propval != LCSP_PROPVAL_AUTO)
w = *lws_csp_px(psb->css_width, psb);
lws_fx_sub(&t1, &w, &re->dlo.box.w);
lws_fx_div(&t1, &t1, &two);
lws_fx_sub(&wd, &t1, &re->dlo.box.x);
lws_fx_add(&re->dlo.box.x, &re->dlo.box.x, &wd);
}
/* fix up the dimensions of div rectangle */
if (!rect) {
lwsl_notice("%s: elem %d: NO RECT\n", __func__, elem_match);
return 1;
}
lhp_set_dlo_adjust_to_contents(ps);
/* if a td, deal with columnar changes in width */
if (ps->dlo->col_list.owner) {
lhp_table_col_t *tc = lws_container_of(
ps->dlo->col_list.owner,
lhp_table_col_t, col_dlos);
lws_fx_t wdelta, ow;
ow = tc->width;
lws_fx_set(tc->width, 0, 0);
/* discover the new width of column */
lws_start_foreach_dll(struct lws_dll2 *, c1,
lws_dll2_get_head(&tc->col_dlos)) {
lws_dlo_t *dloc = lws_container_of(c1,
lws_dlo_t, col_list);
if (lws_fx_comp(&dloc->box.w, &tc->width) > 0)
tc->width = dloc->box.w;
} lws_end_foreach_dll(c1);
/* new width - old column width */
lws_fx_sub(&wdelta, &tc->width, &ow);
/*
* Update all dlos in our column (except
* ourselves) with the increased column width
*/
lws_start_foreach_dll(struct lws_dll2 *, cold,
lws_dll2_get_head(&tc->col_dlos)) {
lws_dlo_t *dloc = lws_container_of(cold,
lws_dlo_t, col_list);
if (dloc != &rect->dlo)
/* we already did this for the
* affected dlo */
lws_fx_add(&dloc->box.w,
&dloc->box.w, &wdelta);
rd = 1;
/* ... and then all of their row-mates
* to the right also need their
* x adjusting then */
while (dloc->row_list.next) {
dloc = lws_container_of(
dloc->row_list.next,
lws_dlo_t, row_list);
lws_fx_add(&dloc->box.x,
&dloc->box.x, &wdelta);
}
} lws_end_foreach_dll(cold);
}
/* if a td, deal with row changes in height */
if (ps->dlo->row_list.owner) {
lhp_table_row_t *tr = lws_container_of(
ps->dlo->row_list.owner,
lhp_table_row_t, row_dlos);
lws_fx_t hdelta, oh;
oh = tr->height;
lws_fx_set(tr->height, 0, 0);
/* discover the new width of column */
lws_start_foreach_dll(struct lws_dll2 *, r1,
lws_dll2_get_head(&tr->row_dlos)) {
lws_dlo_t *dlor = lws_container_of(r1,
lws_dlo_t, row_list);
if (lws_fx_comp(&dlor->box.h, &tr->height) > 0)
tr->height = dlor->box.h;
} lws_end_foreach_dll(r1);
/* new height - old row height */
lws_fx_sub(&hdelta, &tr->height, &oh);
/*
* Update all dlos in our row (except
* ourselves) with the increased row height
*/
lws_start_foreach_dll(struct lws_dll2 *, rold,
lws_dll2_get_head(&tr->row_dlos)) {
lws_dlo_t *dlor = lws_container_of(rold,
lws_dlo_t, row_list);
if (dlor != &rect->dlo)
/* we already did this for the
* affected dlo */
lws_fx_add(&dlor->box.h,
&dlor->box.h, &hdelta);
/* ... so all of their col-mates below
* also need their y adjusting then */
while (dlor->col_list.next) {
dlor = lws_container_of(
dlor->col_list.next,
lws_dlo_t, col_list);
lws_fx_add(&dlor->box.y,
&dlor->box.y, &hdelta);
}
rd = 1;
} lws_end_foreach_dll(rold);
}
/*
* Row dimensions have to be reassessed?
*/
if (rd) {
lws_start_foreach_dll(struct lws_dll2 *, ro,
lws_dll2_get_head(&pst->dlo->children)) {
lws_dlo_t *dlo = lws_container_of(ro, lws_dlo_t, list);
lws_dlo_dim_t dim;
lws_dlo_contents(dlo, &dim);
lws_display_dlo_adjust_dims(dlo, &dim);
} lws_end_foreach_dll(ro);
}
if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) {
/* parent should account for our margin */
if (elem_match == LHP_ELEM_DIV) {
lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
/* now we applied ps->widest, reset it */
lws_fx_set(ps->widest, 0, 0);
psb->dlo_set_curx = ps->dlo;
} else {
/* needed for margin between table cells */
lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_LEFT], ps));
lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
}
if (elem_match != LHP_ELEM_TD) {
if (ps->css_display->propval != LCSP_PROPVAL_INLINE_BLOCK) {
lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->box.h);
psb->dlo_set_cury = ps->dlo;
}
// lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->margin[CCPAS_BOTTOM]);
} else
ps->widest = ps->dlo->box.w;
}
return 0;
}
/*
* Generic LHP displaylist object layout callback... converts html elements
* into DLOs on the display list
*/
lws_stateful_ret_t
lhp_displaylist_layout(lhp_ctx_t *ctx, char reason)
{
lhp_pstack_t *psb = NULL, *pst = NULL, *psp = NULL,
*ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list);
struct lws_context *cx = (struct lws_context *)ctx->user1;
lws_dl_rend_t *drt = (lws_dl_rend_t *)ctx->user;
lws_fx_t br[4], t1, indent, ox, w, h;
const lws_display_font_t *f = NULL;
lhp_table_col_t *tcol = NULL;
lhp_table_row_t *trow = NULL;
lws_dlo_t *abut_x, *abut_y;
uint32_t col = 0xff000000;
lws_dlo_text_t *txt;
const lcsp_atr_t *a;
lws_dlo_image_t u;
const char *pname;
char lastm = 0;
int elem_match;
lws_box_t box;
char url[128];
int n, s = 0;
/* default font choice */
lws_font_choice_t fc = {
.family_name = "term, serif",
.fixed_height = 16,
.weight = 400,
};
if (!ps->font) {
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE);
if (a)
fc.fixed_height = (uint16_t)a->u.i.whole;
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY);
if (a)
fc.family_name = (const char *)&a[1];
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_WEIGHT);
if (a) {
switch (a->propval) {
case LCSP_PROPVAL_BOLD:
fc.weight = 700;
break;
case LCSP_PROPVAL_BOLDER:
fc.weight = 800;
break;
default:
if (a->u.i.whole)
fc.weight = (uint16_t)a->u.i.whole;
break;
}
}
ps->font = lws_font_choose(cx, &fc);
}
f = ps->font;
psb = lws_css_get_parent_block(ctx, ps);
elem_match = 0;
for (n = 0; n < (int)LWS_ARRAY_SIZE(elems); n++)
if (ctx->npos == elems[n].elem_len &&
!memcmp(ctx->buf, elems[n].elem, elems[n].elem_len))
elem_match = n + 1;
switch (reason) {
case LHPCB_CONSTRUCTED:
case LHPCB_DESTRUCTED:
case LHPCB_COMPLETE:
case LHPCB_FAILED:
break;
case LHPCB_ELEMENT_START:
switch (elem_match) {
case LHP_ELEM_BR:
newline(ctx, psb, ps, drt->dl);
break;
case LHP_ELEM_TR:
if (!psb)
break;
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: td: no table found\n", __func__);
break;
}
pst->curx.whole = 0;
pst->curx.frac = 0;
psb->dlo_set_curx = NULL;
trow = lws_zalloc(sizeof(*trow), __func__);
if (!trow) {
lwsl_err("%s: OOM\n", __func__);
return LWS_SRET_FATAL;
}
lws_dll2_add_tail(&trow->list, &pst->dlo->table_rows);
trow = NULL;
pst->td_idx = 0;
goto do_rect;
case LHP_ELEM_TD:
if (!psb) {
lwsl_err("%s: td: no psb found\n", __func__);
break;
}
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: td: no table found\n", __func__);
break;
}
if (pst->td_idx >= (int)pst->dlo->table_cols.count) {
tcol = lws_zalloc(sizeof(*tcol), __func__);
if (!tcol) {
lwsl_err("%s: OOM\n", __func__);
return LWS_SRET_FATAL;
}
lws_dll2_add_tail(&tcol->list, &pst->dlo->table_cols);
} else {
tcol = lws_container_of(pst->dlo->table_cols.head, lhp_table_col_t, list);
n = pst->td_idx;
while (n--)
tcol = lws_container_of(tcol->list.next, lhp_table_col_t, list);
}
if (pst->dlo->table_rows.tail)
trow = lws_container_of(pst->dlo->table_rows.tail, lhp_table_row_t, list);
goto do_rect;
case LHP_ELEM_TABLE:
ps->is_table = 1;
/* fallthru */
case LHP_ELEM_DIV:
do_rect:
lws_fx_set(box.x, 0, 0);
lws_fx_set(box.y, 0, 0);
lws_fx_set(box.h, 0, 0);
lws_fx_set(box.w, 0, 0);
abut_x = NULL;
abut_y = NULL;
if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) {
box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps);
box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps);
} else {
if (psb) {
/* margin adjusts our child box origin */
lws_fx_add(&box.x, &psb->curx,
lws_csp_px(ps->css_margin[CCPAS_LEFT], ps));
box.y = psb->cury;
abut_x = psb->dlo_set_curx;
abut_y = psb->dlo_set_cury;
//lws_fx_add(&box.y, &psb->cury,
// lws_csp_px(ps->css_margin[CCPAS_TOP], ps));
}
}
/* If there's an explicit width, try to go with that */
if (ps->css_width &&
ps->css_width->unit != LCSP_UNIT_NONE &&
lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) < 0)
box.w = *lws_csp_px(ps->css_width, ps);
/* !!! we rely on this being nonzero to not infinite loop at text layout */
lws_fx_add(&box.w, &box.w,
lws_csp_px(ps->css_padding[CCPAS_LEFT], ps));
lws_fx_add(&box.w, &box.w,
lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
ps->drt.w = box.w;
ps->curx = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps);
ps->cury = *lws_csp_px(ps->css_padding[CCPAS_TOP], ps);
memset(br, 0, sizeof(br));
if (ps->css_border_radius[0])
br[0] = *lws_csp_px(ps->css_border_radius[0], ps);
if (ps->css_border_radius[1])
br[1] = *lws_csp_px(ps->css_border_radius[1], ps);
if (ps->css_border_radius[2])
br[2] = *lws_csp_px(ps->css_border_radius[2], ps);
if (ps->css_border_radius[3])
br[3] = *lws_csp_px(ps->css_border_radius[3], ps);
psp = lws_container_of(ps->list.prev, lhp_pstack_t, list);
ps->dlo = (lws_dlo_t *)lws_display_dlo_rect_new(drt->dl,
ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE ? NULL : psp->dlo,
&box, br, ps->css_background_color ?
ps->css_background_color->u.rgba : 0);
if (!ps->dlo) {
lwsl_err("%s: FAILED to create rect\n", __func__);
return LWS_SRET_FATAL;
}
ps->dlo->abut_x = abut_x;
ps->dlo->abut_y = abut_y;
if (psb)
lws_fx_add(&psb->curx, &psb->curx,
lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
if (tcol)
lws_dll2_add_tail(&ps->dlo->col_list, &tcol->col_dlos);
if (trow)
lws_dll2_add_tail(&ps->dlo->row_list, &trow->row_dlos);
lws_lhp_tag_dlo_id(ctx, ps, ps->dlo);
lhp_set_dlo_padding_margin(ps, ps->dlo);
break;
case LHP_ELEM_IMG:
pname = lws_html_get_atr(ps, "src", 3);
if (!psb)
break;
if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) {
box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps);
box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps);
} else {
box.x = psb->curx;
box.y = psb->cury;
}
lws_fx_set(box.x, 0, 0);
lws_fx_set(box.y, 0, 0);
if (psb) {
lws_fx_add(&box.x, &box.x,
lws_csp_px(psb->css_margin[CCPAS_LEFT], psb));
lws_fx_add(&box.y, &box.y,
lws_csp_px(psb->css_margin[CCPAS_TOP], psb));
}
box.h = ctx->ic.wh_px[1]; /* placeholder */
lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
if (ps->css_width &&
lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) > 0)
box.w = *lws_csp_px(ps->css_width, ps);
if (lws_http_rel_to_url(url, sizeof(url),
ctx->base_url, pname))
break;
if (lws_dlo_ss_find(cx, url, &u))
break;
lws_lhp_tag_dlo_id(ctx, ps, (lws_dlo_t *)(u.u.dlo_jpeg));
w = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx,
LCSP_PROP_WIDTH), ps);
h = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx,
LCSP_PROP_HEIGHT), ps);
if (!w.whole || !h.whole) {
w = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w;
h = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w;
}
if (psb) {
lws_fx_add(&psb->curx, &psb->curx, &w);
lws_fx_add(&psb->cury, &psb->cury, &h);
psb->dlo_set_curx = ps->dlo;
psb->dlo_set_cury = ps->dlo;
if (lws_fx_comp(&psb->curx, &psb->widest) > 0)
psb->widest = psb->curx;
}
break;
default:
break;
}
break;
case LHPCB_ELEMENT_END:
if (ctx->npos == 2 && ctx->buf[0] == 'h' &&
ctx->buf[1] > '0' && ctx->buf[1] <= '6') {
if (!psb)
break;
newline(ctx, psb, ps, drt->dl);
lws_fx_add(&psb->cury, &psb->cury,
lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps));
lws_fx_add(&psb->cury, &psb->cury,
lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
break;
}
switch (elem_match) {
case LHP_ELEM_TR:
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: /td: no table\n", __func__);
break;
}
pst->tr_idx++;
pst->td_idx = 0;
goto do_end_rect;
case LHP_ELEM_TD:
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: /td: no table\n", __func__);
break;
}
pst->td_idx++;
goto do_end_rect;
/* fallthru */
case LHP_ELEM_TABLE:
case LHP_ELEM_DIV:
do_end_rect:
ox = ps->curx;
if (lws_fx_comp(&ox, &ps->widest) > 0)
ps->widest = ox;
newline(ctx, ps, ps, drt->dl);
if (lws_lhp_dlo_adjust_div_type_element(ctx, psb, pst, ps, elem_match))
break;
if (lws_fx_comp(&ps->curx, &ps->widest) > 0)
ps->widest = ps->curx;
/* move parent on according to used area plus bottom margin */
if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) {
switch (ps->css_display->propval) {
case LCSP_PROPVAL_BLOCK:
case LCSP_PROPVAL_TABLE:
case LCSP_PROPVAL_TABLE_ROW:
lws_fx_set(psb->curx, 0, 0);
psb->dlo_set_curx = NULL;
if (ps->css_display->propval == LCSP_PROPVAL_TABLE_ROW)
break;
lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
break;
case LCSP_PROPVAL_INLINE_BLOCK:
//lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
lws_fx_set(ps->widest, 0, 0);
psb->dlo_set_curx = ps->dlo;
psb->dlo_set_cury = ps->dlo;
break;
default:
lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
psb->dlo_set_curx = ps->dlo;
break;
}
if (lws_fx_comp(&psb->curx, &psb->widest) > 0)
psb->widest = psb->curx;
}
ps->dlo = NULL;
break;
default:
break;
}
break;
case LHPCB_CONTENT:
if (!ps->css_display ||
ps->css_display->propval == LCSP_PROPVAL_NONE)
break;
if (ps->css_color)
col = ps->css_color->u.rgba;
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE);
if (a)
fc.fixed_height = (uint16_t)a->u.i.whole;
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY);
if (a)
fc.family_name = (const char *)&a[1];
for (n = 0; n < ctx->npos; n++)
if (ctx->buf[n] == '\n')
s++;
if (s == ctx->npos)
return 0;
/*
* Let's not deal with things off the bottom of the display
* surface.
*/
if (psb && psb->cury.whole > ctx->ic.wh_px[LWS_LHPREF_HEIGHT].whole)
return 0;
if (!psb)
return 0;
f = lws_font_choose(cx, &fc);
n = s;
while (n < ctx->npos) {
int m;
lws_fx_set(box.x, 0, 0);
lws_fx_set(box.y, 0, 0);
lws_fx_set(box.w, 0, 0);
if (n == s && !(psb->runon & 1)) {
lws_fx_set(indent, 0, 0);
} else
indent = psb->curx;
lws_fx_add(&box.x, &indent,
lws_csp_px(ps->css_padding[CCPAS_LEFT], ps));
lws_fx_add(&box.y, &box.y, &psb->cury);
box.h.whole = (int32_t)f->choice.fixed_height;
box.h.frac = 0;
if (psb->css_width &&
(psb->css_width->propval == LCSP_PROPVAL_AUTO ||
ps->css_width->propval == LCSP_PROPVAL_AUTO)) {
//lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
box.w = ctx->ic.wh_px[0];
} else {
lws_fx_sub(&t1, &psb->drt.w,
lws_csp_px(psb->css_padding[CCPAS_LEFT], psb));
lws_fx_sub(&box.w, &t1,
lws_csp_px(psb->css_padding[CCPAS_RIGHT], psb));
}
if (!box.w.whole)
lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
assert(psb);
txt = lws_display_dlo_text_new(drt->dl,
(lws_dlo_t *)psb->dlo, &box, f);
if (!txt) {
lwsl_err("%s: failed to alloc text\n", __func__);
return 1;
}
runon(psb, &txt->dlo);
txt->flags |= LWSDLO_TEXT_FLAG_WRAP;
lhp_set_dlo_padding_margin(ps, &txt->dlo);
// a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN);
//lwsl_hexdump_notice(ctx->buf + n, (size_t)(ctx->npos - n));
m = lws_display_dlo_text_update(txt, col, indent,
ctx->buf + n,
(size_t)(ctx->npos - n));
if (m < 0) {
lwsl_err("text_update ret %d\n", m);
break;
}
if (m == 2 && lastm)
return 0;
lastm = m == 2;
n = (int)((size_t)n + txt->text_len);
txt->dlo.box.w = txt->bounding_box.w;
txt->dlo.box.h = txt->bounding_box.h;
lws_fx_add(&psb->curx, &psb->curx, &txt->bounding_box.w);
psb->dlo_set_curx = &txt->dlo;
//lwsl_user("%s: bounding width %d, m: %d, text %.*s\n",
// __func__, txt->bounding_box.w.whole, m,
// ctx->npos, ctx->buf);
if (m > 0) { /* wrapping */
newline(ctx, psb, ps, drt->dl);
lws_fx_set(ps->curx, 0, 0);
lws_fx_set(psb->curx, 0, 0);
psb->dlo_set_curx = NULL;
lws_fx_add(&ps->cury, &ps->cury, &txt->bounding_box.h);
psb->dlo_set_cury = &txt->dlo;
}
}
break;
case LHPCB_COMMENT:
break;
}
return 0;
}

View File

@ -0,0 +1,184 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: PNG
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
void
lws_display_dlo_png_destroy(struct lws_dlo *dlo)
{
lws_dlo_png_t *dlo_png = lws_container_of(dlo, lws_dlo_png_t, dlo);
#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS)
lws_ss_destroy(&dlo_png->flow.h);
#endif
lws_buflist_destroy_all_segments(&dlo_png->flow.bl);
if (dlo_png->png)
lws_upng_free(&dlo_png->png);
}
lws_stateful_ret_t
lws_display_render_png(struct lws_display_render_state *rs)
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_png_t *dlo_png = lws_container_of(dlo, lws_dlo_png_t, dlo);
lws_fx_t ax, ay, t, t1;
lws_display_colour_t pc;
lws_stateful_ret_t r;
const uint8_t *pix;
int s, e;
if (!lws_upng_get_height(dlo_png->png)) {
lwsl_info("%s: png does not have dimensions yet\n", __func__);
return LWS_SRET_WANT_INPUT;
}
lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&t, &ax, &dlo->box.w);
lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&t1, &ay, &dlo->box.h);
s = ax.whole;
e = lws_fx_roundup(&t);
if (rs->curr > lws_fx_roundup(&t1))
return LWS_SRET_OK;
if (rs->curr - lws_fx_roundup(&ay) >
(int)lws_upng_get_height(dlo_png->png))
return LWS_SRET_OK;
if (s < 0)
s = 0;
if (s > rs->ic->wh_px[0].whole)
return LWS_SRET_OK; /* off to the right */
if (e > rs->ic->wh_px[0].whole)
e = rs->ic->wh_px[0].whole - 1;
if (e <= 0)
return LWS_SRET_OK; /* off to the left */
do {
if (lws_flow_feed(&dlo_png->flow))
/* if he says WANT_INPUT, we have nothing in the buflist */
return LWS_SRET_WANT_INPUT;
pix = NULL;
r = lws_upng_emit_next_line(dlo_png->png, &pix, &dlo_png->flow.data,
&dlo_png->flow.len, rs->html == 1 /* hold at metadata */);
if (r & LWS_SRET_NO_FURTHER_IN)
dlo_png->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
if (r & (LWS_SRET_FATAL | LWS_SRET_YIELD) || r == LWS_SRET_OK)
return r;
r = lws_flow_req(&dlo_png->flow);
if (r & LWS_SRET_WANT_INPUT)
return r;
} while (!pix);
pix = pix + (( (unsigned int)(s - ax.whole) *
(lws_upng_get_pixelsize(dlo_png->png) / 8)));
while (s < e && s >= ax.whole && s < lws_fx_roundup(&t) &&
(s - ax.whole) < (int)lws_upng_get_width(dlo_png->png)) {
if (lws_upng_get_pixelsize(dlo_png->png))
pc = LWSDC_RGBA(pix[0], pix[0], pix[0], pix[1]);
pc = LWSDC_RGBA(pix[0], pix[1], pix[2], pix[3]);
lws_surface_set_px(rs->ic, rs->line, s, &pc);
s++;
pix += lws_upng_get_pixelsize(dlo_png->png) / 8;
}
return LWS_SRET_OK;
}
lws_stateful_ret_t
lws_display_dlo_png_metadata_scan(lws_dlo_png_t *dlo_png)
{
lws_stateful_ret_t r;
size_t l, l1;
const uint8_t *pix;
/*
* If we don't have the image metadata yet, provide small chunks of the
* source data until we do have the image metadata, but small enough
* we can't produce any decoded pixels too early.
*/
while (!lws_upng_get_height(dlo_png->png) && dlo_png->flow.len) {
l1 = l = dlo_png->flow.len > 33 ? 33 : dlo_png->flow.len;
r = lws_upng_emit_next_line(dlo_png->png, &pix, &dlo_png->flow.data, &l, 1);
if (r & LWS_SRET_FATAL) {
lwsl_err("%s: hdr parse failed\n", __func__);
return r;
}
dlo_png->flow.len -= l1 - l;
if (lws_upng_get_height(dlo_png->png)) {
lwsl_info("png: w %d, h %d\n",
lws_upng_get_width(dlo_png->png),
lws_upng_get_height(dlo_png->png));
return LWS_SRET_OK;
}
}
return LWS_SRET_WANT_INPUT;
}
lws_dlo_png_t *
lws_display_dlo_png_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box)
{
lws_dlo_png_t *dlo_png = lws_zalloc(sizeof(*dlo_png), __func__);
dlo_png->png = lws_upng_new();
if (!dlo_png->png)
goto bail;
dlo_png->dlo.box = *box;
dlo_png->dlo.render = lws_display_render_png;
dlo_png->dlo._destroy = lws_display_dlo_png_destroy;
lws_display_dlo_add(dl, dlo_parent, &dlo_png->dlo);
return dlo_png;
bail:
if (dlo_png->png)
lws_upng_free(&dlo_png->png);
lws_free(dlo_png);
return NULL;
}

View File

@ -0,0 +1,247 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: rect / rounded rect
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
/* returns where on the x axis we intercept ys (== (curr - ory) ^ 2 ) */
static void
isect(lws_circle_t *c, lws_fx_t *f, lws_fx_t *axsq)
{
assert(axsq->whole >= 0);
assert(c->rsq.whole >= 0);
lws_fx_sub(f, &c->rsq, axsq);
if (f->whole < 0) {
f->whole = 0;
f->frac = 0;
} else
lws_fx_sqrt(f, f);
lws_fx_sub(f, &c->r, f);
}
/* give it absolute x, returns intersection point as absolute y*/
static void
isect_y_from_x(lws_circle_t *c, lws_fx_t *x, lws_fx_t *y)
{
lws_fx_t t, t1;
lws_fx_sub(y, x, &c->orx);
lws_fx_mul(&t, y, y);
lws_fx_sub(&t1, &c->rsq, &t);
lws_fx_sqrt(&t, &t1);
lws_fx_add(y, &c->ory, &t);
}
lws_stateful_ret_t
lws_display_render_rect(struct lws_display_render_state *rs)
/* const lws_surface_info_t *ic, struct lws_dlo *dlo,
const lws_box_t *origin, lws_display_scalar curr,
uint8_t *line, lws_colour_error_t **nle) */
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_rect_t *r = lws_container_of(dlo, lws_dlo_rect_t, dlo);
lws_fx_t cf, y, w, trim, s, e, t2, sfy;
lws_display_colour_t dc;
int n, le, os;
if (!LWSDC_ALPHA(dlo->dc))
return LWS_SRET_OK;
if (!r->init) {
lws_fx_add(&r->db.x, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&r->db.y, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&r->right, &r->db.x, &dlo->box.w);
lws_fx_add(&r->btm, &r->db.y, &dlo->box.h);
lws_fx_add(&r->c[0].ory, &r->db.y, &r->c[0].r);
lws_fx_add(&r->c[1].ory, &r->db.y, &r->c[1].r);
lws_fx_sub(&r->c[2].ory, &r->btm, &r->c[2].r);
lws_fx_sub(&r->c[3].ory, &r->btm, &r->c[3].r);
lws_fx_add(&r->c[0].orx, &r->db.x, &r->c[0].r);
lws_fx_sub(&r->c[1].orx, &r->right, &r->c[1].r);
lws_fx_add(&r->c[2].orx, &r->db.x, &r->c[2].r);
lws_fx_sub(&r->c[3].orx, &r->right, &r->c[3].r);
r->init = 1;
}
if (lws_fx_comp(&r->db.x, &rs->ic->wh_px[0]) >= 0)
return LWS_SRET_OK; /* off to the right */
if (rs->curr < r->db.y.whole - 1 || rs->curr > lws_fx_roundup(&r->btm))
return LWS_SRET_OK;
s = r->db.x;
lws_fx_add(&e, &s, &dlo->box.w);
cf.whole = rs->curr;
cf.frac = 50000000;
/*
* Account for four independently radiused corners
*
* Fractional pixel occupancy is represented by modulating alpha.
*
* We know that the subpixel intersection on the circle is at yo.frac +
* radius.frac which usually won't align to any pixel boundary.
*/
for (n = 0; n < 4; n++) {
lws_fx_sub(&y, &cf, &r->c[n].ory);
lws_fx_mul(&r->c[n].ys, &y, &y);
}
/* For this y line, find out how many x pixels we can skip at start
* and end before and after the first pixels that intersect */
if (rs->curr <= (r->c[0].ory.whole)) { /* top left trims s */
isect(&r->c[0], &trim, &r->c[0].ys /* (cf - ory)^2 */);
lws_fx_add(&s, &s, &trim);
}
if (rs->curr <= (r->c[1].ory.whole)) { /* top right trims e */
isect(&r->c[1], &trim, &r->c[1].ys);
lws_fx_sub(&e, &e, &trim);
}
if (rs->curr >= (r->c[2].ory.whole)) { /* bottom left trims s */
isect(&r->c[2], &trim, &r->c[2].ys);
lws_fx_add(&s, &s, &trim);
}
if (rs->curr >= (r->c[3].ory.whole)) { /* bottom right trims e */
isect(&r->c[3], &trim, &r->c[3].ys);
lws_fx_sub(&e, &e, &trim);
}
/* clips */
if (s.whole < 0)
lws_fx_set(s, 0, 0);
if (e.whole >= rs->ic->wh_px[0].whole)
lws_fx_set(e, rs->ic->wh_px[0].whole - 1, 0);
if (e.whole <= 0 || e.whole < s.whole)
return LWS_SRET_OK; /* off to the left */
lws_fx_sub(&w, &e, &s);
if (lws_fx_comp(&w, &dlo->box.w) > 0)
lws_fx_add(&e, &s, &dlo->box.w);
/* render the part of the line occupied by the rect body */
sfy = s;
os = s.whole;
s.frac = 0;
le = e.whole + 1;
while (s.whole <= le) {
unsigned int alpha = dlo->dc >> 24;
if (rs->curr <= r->c[0].ory.whole - 1 && s.whole >= r->db.x.whole &&
lws_fx_comp(&s, &r->c[0].orx) <= 0) {
isect_y_from_x(&r->c[0], &s, &t2);
lws_fx_sub(&t2, &t2, &r->c[0].r);
lws_fx_sub(&t2, &t2, &r->c[0].r);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (rs->curr <= (r->c[1].ory.whole - 1) &&
s.whole >= r->c[1].orx.whole) {
isect_y_from_x(&r->c[1], &s, &t2);
lws_fx_sub(&t2, &t2, &r->c[1].r);
lws_fx_sub(&t2, &t2, &r->c[1].r);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (rs->curr >= (r->c[2].ory.whole + 1) &&
s.whole < lws_fx_roundup(&r->c[2].orx)) {
isect_y_from_x(&r->c[2], &s, &t2);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (rs->curr >= (r->c[3].ory.whole + 1) &&
s.whole >= lws_fx_roundup(&r->c[3].orx)) {
isect_y_from_x(&r->c[3], &s, &t2);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (s.whole == os && sfy.frac)
alpha = (((uint64_t)(99999999 - sfy.frac) * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
if (s.whole == le)
alpha = (((uint64_t)e.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
dc = (lws_display_colour_t)(((dlo->dc & 0xffffff) |
(uint32_t)(alpha << 24)));
lws_surface_set_px(rs->ic, rs->line, s.whole, &dc);
s.whole++;
}
return LWS_SRET_OK;
}
lws_dlo_rect_t *
lws_display_dlo_rect_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box, const lws_fx_t *radii,
lws_display_colour_t dc)
{
lws_dlo_rect_t *r = lws_zalloc(sizeof(*r), __func__);
int n;
if (!r)
return NULL;
r->dlo.render = lws_display_render_rect;
r->dlo.box = *box;
r->dlo.dc = dc;
if (radii) {
r->c[0].r = radii[0];
r->c[1].r = radii[1];
r->c[2].r = radii[2];
r->c[3].r = radii[3];
for (n = 0; n < 4; n++)
lws_fx_mul(&r->c[n].rsq, &r->c[n].r, &r->c[n].r);
}
lws_display_dlo_add(dl, dlo_parent, &r->dlo);
return r;
}

View File

@ -0,0 +1,342 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Secure Streams as DLO transport
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
LWS_SS_USER_TYPEDEF
sul_cb_t on_rx;
lhp_ctx_t *lhp;
lws_sorted_usec_list_t *ssevsul; /* sul to use to resume rz */
lws_sorted_usec_list_t sul; /* used for initial metadata cb */
lws_dlo_image_t u; /* we use the lws_flow_t in here */
lws_dll2_t active_asset_list; /*cx->active_assets*/
uint8_t type; /* LWSDLOSS_TYPE_ */
char url[96];
} dloss_t;
/*
* dlo images call back here when they have their dimensions (or have failed)
*/
void
lws_lhp_image_dimensions_cb(lws_sorted_usec_list_t *sul)
{
dloss_t *m = lws_container_of(sul, dloss_t, sul);
lws_display_render_state_t *rs = lws_container_of(m->ssevsul,
lws_display_render_state_t, sul);
lws_dlo_dim_t dim;
lws_dlo_t *dlo = &m->u.u.dlo_png->dlo;
if (m->u.failed) {
dlo->box.w.whole = -1;
dlo->box.h.whole = -1;
lwsl_notice("%s: Failing %s\n", __func__, m->url);
} else {
dlo->box.w.whole = (int32_t)lws_dlo_image_width(&m->u);
dlo->box.h.whole = (int32_t)lws_dlo_image_height(&m->u);
lwsl_err("%s: setting dlo box %d x %d\n", __func__,
(int)dlo->box.w.whole, (int)dlo->box.h.whole);
#if 1
lws_dlo_contents(dlo, &dim);
lws_display_dlo_adjust_dims(dlo, &dim);
if (dlo->list.owner) {
dlo = lws_container_of(dlo->list.owner, lws_dlo_t, children);
lws_dlo_contents(dlo, &dim);
lws_display_dlo_adjust_dims(dlo, &dim);
}
#endif
}
if (rs->html != 1) {
lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1);
return;
}
/* we are resuming the html parsing */
lws_lhp_ss_html_parse_from_lhp(m->lhp);
}
/* secure streams payload interface */
static lws_ss_state_return_t
dloss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
{
dloss_t *m = (dloss_t *)userobj;
lws_stateful_ret_t r;
lwsl_info("%s: %u\n", __func__, (unsigned int)len);
if (m->type == LWSDLOSS_TYPE_CSS) {
m->lhp->finish_css = !!(flags & LWSSS_FLAG_EOM);
m->lhp->is_css = 1;
r = lws_lhp_parse(m->lhp, &buf, &len);
m->lhp->is_css = 0;
if (flags & LWSSS_FLAG_EOM)
lws_dll2_remove(&m->active_asset_list);
if (r & LWS_SRET_FATAL)
return LWSSSSRET_DISCONNECT_ME;
if (r & LWS_SRET_AWAIT_RETRY) {
lwsl_warn("%s: returning to await retry\n", __func__);
if (!m->lhp->await_css_done)
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
m->lhp->sshtmlevsul,
m->lhp->sshtmlevcb, 1);
}
goto okie;
}
/* .flow is at the same offset in both dlo_jpeg and dlo_png */
if (len &&
lws_buflist_append_segment(&m->u.u.dlo_jpeg->flow.bl, buf, len) < 0) {
m->u.failed = 1;
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
&m->sul, lws_lhp_image_dimensions_cb, 1);
return LWSSSSRET_DISCONNECT_ME;
}
// lwsl_notice("%s: buflen size %d\n", __func__,
// (int)lws_buflist_total_len(&m->u.u.dlo_jpeg->flow.bl));
if (flags & LWSSS_FLAG_EOM) {
m->u.u.dlo_jpeg->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
return LWSSSSRET_DISCONNECT_ME;
}
if (!lws_dlo_image_width(&m->u)) {
lws_flow_feed(&m->u.u.dlo_jpeg->flow);
r = lws_dlo_image_metadata_scan(&m->u);
lws_flow_req(&m->u.u.dlo_jpeg->flow);
if (r & LWS_SRET_FATAL) {
m->u.failed = 1;
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
&m->sul, lws_lhp_image_dimensions_cb, 1);
return LWSSSSRET_DISCONNECT_ME;
}
if (r != LWS_SRET_WANT_INPUT) {
lwsl_notice("%s: seen metadata\n", __func__);
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
&m->sul, lws_lhp_image_dimensions_cb, 1);
} //else
//lwsl_err("%s: metadata scan no end yet\n", __func__);
return LWSSSSRET_OK;
}
okie:
lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1);
return LWSSSSRET_OK;
}
static lws_ss_state_return_t
dloss_state(void *userobj, void *sh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack)
{
dloss_t *m = (dloss_t *)userobj;
switch (state) {
case LWSSSCS_CREATING:
break;
case LWSSSCS_DESTROYING:
lws_sul_cancel(&m->sul);
lws_dll2_remove(&m->active_asset_list);
break;
default:
break;
}
return LWSSSSRET_OK;
}
static LWS_SS_INFO("__default", dloss_t)
.rx = dloss_rx,
.state = dloss_state
};
/*
* If we have an active image asset from this URL, return a pointer to its
* dlo image (ie, dlo_jpeg or dlo_png)
*/
int
lws_dlo_ss_find(struct lws_context *cx, const char *url, lws_dlo_image_t *u)
{
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&cx->active_assets)) {
dloss_t *ds = lws_container_of(d, dloss_t, active_asset_list);
if (!strcmp(url, ds->url)) {
*u = ds->u;
return 0; /* found */
}
} lws_end_foreach_dll(d);
return 1; /* not found */
}
int
lws_dlo_ss_create(lws_dlo_ss_create_info_t *i, lws_dlo_t **pdlo)
{
lws_dlo_jpeg_t *dlo_jpeg = NULL;
lws_dlo_png_t *dlo_png = NULL;
size_t ul = strlen(i->url);
struct lws_ss_handle *h;
lws_dlo_t *dlo = NULL;
lws_ss_info_t ssi;
dloss_t *dloss;
uint8_t type;
if (ul < 5)
return 1;
if (!strcmp(i->url + ul - 4, ".png"))
type = LWSDLOSS_TYPE_PNG;
else
if (!strcmp(i->url + ul - 4, ".jpg") ||
!strcmp(i->url + ul - 5, ".jpeg"))
type = LWSDLOSS_TYPE_JPEG;
else
if (!strcmp(i->url + ul - 4, ".css"))
type = LWSDLOSS_TYPE_CSS;
else {
lwsl_err("%s: unknown file type %s\n", __func__, i->url);
return 1;
}
switch (type) {
case LWSDLOSS_TYPE_PNG:
dlo_png = lws_display_dlo_png_new(i->dl, i->dlo_parent, i->box);
if (!dlo_png)
return 1;
i->u->u.dlo_png = dlo_png;
dlo_png->dlo.box.w.whole = (int32_t)
lws_upng_get_width(dlo_png->png);
dlo_png->dlo.box.w.frac = 0;
dlo_png->dlo.box.h.whole = (int32_t)
lws_upng_get_height(dlo_png->png);
dlo_png->dlo.box.h.frac = 0;
dlo = &dlo_png->dlo;
break;
case LWSDLOSS_TYPE_JPEG:
dlo_jpeg = lws_display_dlo_jpeg_new(i->dl, i->dlo_parent, i->box);
if (!dlo_jpeg)
return 1;
i->u->u.dlo_jpeg = dlo_jpeg;
dlo_jpeg->dlo.box.w.whole = (int32_t)
lws_jpeg_get_width(dlo_jpeg->j);
dlo_jpeg->dlo.box.w.frac = 0;
dlo_jpeg->dlo.box.h.whole = (int32_t)
lws_jpeg_get_height(dlo_jpeg->j);
dlo_jpeg->dlo.box.h.frac = 0;
dlo = &dlo_jpeg->dlo;
break;
}
/* we adapt the initial tx credit also to the requested window */
ssi = ssi_dloss_t;
ssi.manual_initial_tx_credit = i->window;
if (lws_ss_create(i->cx, 0, &ssi, (void *)dlo, &h, NULL, NULL)) {
lwsl_notice("%s: unable to create ss\n", __func__);
return 1;
}
dloss = (dloss_t *)lws_ss_to_user_object(h);
dloss->u.type = (lws_dlo_image_type_t)type;
dloss->on_rx = i->on_rx;
dloss->ssevsul = i->on_rx_sul;
dloss->lhp = i->lhp;
dloss->type = type;
lws_strncpy(dloss->url, i->url, sizeof(dloss->url));
switch (type) {
case LWSDLOSS_TYPE_PNG:
dloss->u.u.dlo_png = dlo_png;
dlo_png->flow.h = h;
dlo_png->flow.window = i->window;
break;
case LWSDLOSS_TYPE_JPEG:
dloss->u.u.dlo_jpeg = dlo_jpeg;
dlo_jpeg->flow.h = h;
dlo_jpeg->flow.window = i->window;
break;
}
if (lws_ss_alloc_set_metadata(h, "endpoint", i->url, ul)) {
lwsl_err("%s: unable to set endpoint\n", __func__);
goto fail;
}
if (lws_ss_client_connect(dloss->ss)) {
lwsl_err("%s: unable to do client connection\n", __func__);
goto fail;
}
lws_dll2_add_tail(&dloss->active_asset_list, &i->cx->active_assets);
lwsl_notice("%s: starting %s (dlo %p)\n", __func__, i->url, dlo);
*pdlo = dlo;
return 0;
fail:
lws_ss_destroy(&h);
switch (type) {
case LWSDLOSS_TYPE_PNG:
lws_display_dlo_png_destroy(&dlo_png->dlo);
break;
case LWSDLOSS_TYPE_JPEG:
lws_display_dlo_jpeg_destroy(&dlo_jpeg->dlo);
break;
}
return 1;
}

View File

@ -0,0 +1,413 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: text
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
size_t
utf8_bytes(uint8_t u)
{
if ((u & 0x80) == 0)
return 1;
if ((u & 0xe0) == 0xc0)
return 2;
if ((u & 0xf0) == 0xe0)
return 3;
if ((u & 0xf8) == 0xf0)
return 4;
return 0;
}
static int
utf8_unicode(const char *utf8, size_t *utf8_len, uint32_t *unicode)
{
size_t glyph_len = utf8_bytes((uint8_t)*utf8);
size_t n;
if (!glyph_len || glyph_len > *utf8_len) {
(*utf8_len)--;
return 1;
}
if (glyph_len == 1)
*unicode = (uint32_t)*utf8++;
else {
*unicode = (uint32_t)((*utf8++) & (0x7f >> glyph_len));
for (n = 1; n < glyph_len; n++)
*unicode = (*unicode << 6) | ((*utf8++) & 0x3f);
}
*utf8_len -= glyph_len;
return 0;
}
void
lws_display_dlo_text_destroy(struct lws_dlo *dlo)
{
lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
lws_free_set_NULL(text->kern);
lws_free_set_NULL(text->text);
lwsac_free(&text->ac_glyphs);
}
int
lws_display_dlo_text_update(lws_dlo_text_t *text, lws_display_colour_t dc,
lws_fx_t indent, const char *utf8, size_t text_len)
{
const char *last_utf8 = utf8, *outf8 = utf8;
size_t last_bp_n = 0, tlen = text_len;
lws_fx_t t1, eff, last_bp_eff, t2;
uint8_t r = 0;
char uc;
if (text->kern)
lws_free_set_NULL(text->kern);
if (text->text)
lws_free_set_NULL(text->text);
lws_dll2_owner_clear(&text->glyphs);
lwsac_free(&text->ac_glyphs);
text->indent = indent;
text->dlo.dc = dc;
lws_fx_set(eff, 0, 0);
/*
* Let's go through the new string glyph by glyph, we want to
* calculate effective kerned widths, and optionally deal with wrapping.
*
* But we don't want to instantiate the glyph objects until we are
* engaged with rendering them. Otherwise we will carry around the
* whole page-worth's of glyphs at once needlessly, which won't scale
* for text-heavy pages. lws_display_dlo_text_attach_glyphs() does the
* same flow as this but to create the glyphs and is called later
* as the text dlo becomes rasterized during rendering.
*/
/* { char b1[22]; lwsl_err("eff %s\n", lws_fx_string(&eff, b1, sizeof(b1))); }
{ char b1[22]; lwsl_err("indent %s\n", lws_fx_string(&indent, b1, sizeof(b1))); }
{ char b1[22]; lwsl_err("boxw %s\n", lws_fx_string(&text->dlo.box.w, b1, sizeof(b1))); } */
while (tlen &&
lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) < 0) {
size_t ot = tlen;
uint32_t unicode;
if (!utf8_unicode(utf8, &tlen, &unicode)) {
text->font->image_glyph(text, unicode, 0);
uc = *utf8;
utf8 += (ot - tlen);
if (uc == ' ') { /* act to snip it if used */
last_utf8 = utf8;
last_bp_n = tlen;
last_bp_eff = eff;
}
if (!lws_display_font_mcufont_getcwidth(text, unicode, &t2))
lws_fx_add(&eff, &eff, &t2);
if (uc == '-' || uc == ',' || uc == ';' || uc == ':') {
/* act to leave it in */
last_utf8 = utf8;
last_bp_n = tlen;
last_bp_eff = eff;
}
} else
lwsl_err("%s: missing glyph\n", __func__);
}
if (last_bp_n &&
lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) >= 0) {
eff = last_bp_eff;
utf8 = last_utf8;
tlen = last_bp_n;
r = 1;
}
text->text_len = text_len - tlen;
if (tlen == text_len) {
lwsl_notice("we couldn't fit anything in there, newline\n");
return 2;
}
text->text = lws_malloc(text->text_len + 1, __func__);
if (!text->text)
return -1;
memcpy(text->text, outf8, text->text_len);
text->text[text->text_len] = '\0';
memset(&text->bounding_box, 0, sizeof(text->bounding_box));
text->bounding_box.w = eff;
text->bounding_box.h.whole = text->font_height;
text->bounding_box.h.frac = 0;
return r;
}
int
lws_display_dlo_text_attach_glyphs(lws_dlo_text_t *text)
{
const char *utf8 = text->text;
size_t tlen = text->text_len;
lws_font_glyph_t *g = NULL;
uint32_t unicode;
lws_fx_t eff;
uint8_t r = 0;
lws_fx_set(eff, 0, 0);
while (tlen) {
size_t ot = tlen;
g = NULL;
if (!utf8_unicode(utf8, &tlen, &unicode))
/* instantiate the glyphs this time */
g = text->font->image_glyph(text, unicode, 1);
if (g == NULL) {
lwsl_warn("%s: no glyph for 0x%02X '%c'\n", __func__, (unsigned int)*utf8, *utf8);
break;
}
utf8 += (ot - tlen);
g->xpx = eff;
lws_fx_add(&eff, &eff, &g->cwidth);
}
return r;
}
lws_dlo_text_t *
lws_display_dlo_text_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box, const lws_display_font_t *font)
{
lws_dlo_text_t *text = lws_zalloc(sizeof(*text), __func__);
if (!text)
return NULL;
text->dlo.render = font->renderer;
text->dlo._destroy = lws_display_dlo_text_destroy;
text->dlo.box = *box;
text->font = font;
lws_display_dlo_add(dl, dlo_parent, &text->dlo);
return text;
}
static const char *
castrstr(const char *haystack, const char *needle)
{
size_t sn = strlen(needle), h = strlen(haystack) - sn + 1, n;
char c, c1;
while (1) {
for (n = 0; n < sn; n++) {
c = (char)((haystack[h + n] >= 'A' && haystack[h + n] <= 'Z') ?
haystack[h + n] + ('a' - 'A') : haystack[h + n]);
c1 = (char)((needle[n] >= 'A' && needle[n] <= 'Z') ?
needle[n] + ('a' - 'A') : needle[n]);
if (c != c1)
break;
}
if (n == sn)
return &haystack[h];
if (!h)
break;
h--;
}
return NULL;
}
int
lws_font_register(struct lws_context *cx, const uint8_t *data, size_t data_len)
{
lws_display_font_t *a;
if (lws_ser_ru32be(data) != LWS_FOURCC('M', 'C', 'U', 'F'))
return 1;
a = lws_zalloc(sizeof(*a), __func__);
if (!a)
return 1;
a->choice.family_name = (const char *)data +
lws_ser_ru32be(data + MCUFO_FOFS_FULLNAME);
if (castrstr(a->choice.family_name, "serif") ||
castrstr(a->choice.family_name, "roman"))
a->choice.generic_name = "serif";
else
a->choice.generic_name = "sans";
if (castrstr(a->choice.family_name, "italic") ||
castrstr(a->choice.family_name, "oblique"))
a->choice.style = 1;
if (castrstr(a->choice.family_name, "extrabold") ||
castrstr(a->choice.family_name, "extra bold"))
a->choice.weight = 900;
else
if (castrstr(a->choice.family_name, "bold"))
a->choice.weight = 700;
else
if (castrstr(a->choice.family_name, "extralight") ||
castrstr(a->choice.family_name, "extra light"))
a->choice.weight = 200;
else
if (castrstr(a->choice.family_name, "light"))
a->choice.weight = 300;
else
a->choice.weight = 400;
a->choice.fixed_height = lws_ser_ru16be(data + MCUFO16_LINE_HEIGHT);
a->data = data;
a->data_len = data_len;
a->renderer = lws_display_font_mcufont_render;
a->image_glyph = lws_display_font_mcufont_image_glyph;
{
lws_dlo_text_t t;
memset(&t, 0, sizeof(t));
t.font = a;
lws_display_font_mcufont_getcwidth(&t, 'm', &a->em);
a->ex.whole = a->choice.fixed_height;
a->ex.frac = 0;
}
lws_dll2_clear(&a->list);
lws_dll2_add_tail(&a->list, &cx->fonts);
return 0;
}
static int
lws_font_destroy(struct lws_dll2 *d, void *user)
{
lws_free(d);
return 0;
}
void
lws_fonts_destroy(struct lws_context *cx)
{
lws_dll2_foreach_safe(&cx->fonts, NULL, lws_font_destroy);
}
struct track {
const lws_font_choice_t *hints;
const lws_display_font_t *best;
int best_score;
};
static int
lws_fonts_score(struct lws_dll2 *d, void *user)
{
const lws_display_font_t *f = lws_container_of(d, lws_display_font_t,
list);
struct track *t = (struct track *)user;
struct lws_tokenize ts;
int score = 1000;
if (t->hints->family_name) {
memset(&ts, 0, sizeof(ts));
ts.start = t->hints->family_name;
ts.len = strlen(ts.start);
ts.flags = LWS_TOKENIZE_F_COMMA_SEP_LIST;
do {
ts.e = (int8_t)lws_tokenize(&ts);
if (ts.e == LWS_TOKZE_TOKEN) {
if (!strncmp(f->choice.family_name, ts.token,
ts.token_len)) {
score = 0;
break;
}
if (f->choice.generic_name &&
!strncmp(f->choice.generic_name, ts.token,
ts.token_len)) {
score -= 500;
break;
}
}
} while (ts.e > 0);
}
if (t->hints->weight)
score += (t->hints->weight > f->choice.weight ?
(t->hints->weight - f->choice.weight) :
(f->choice.weight - t->hints->weight)) / 100;
if (t->hints->style != f->choice.style)
score += 100;
if (t->hints->fixed_height)
score += 10 * (t->hints->fixed_height > f->choice.fixed_height ?
(t->hints->fixed_height - f->choice.fixed_height) :
(f->choice.fixed_height - t->hints->fixed_height));
if (score < t->best_score) {
t->best_score = score;
t->best = f;
}
return 0;
}
const lws_display_font_t *
lws_font_choose(struct lws_context *cx, const lws_font_choice_t *hints)
{
struct track t;
t.hints = hints;
t.best = (const lws_display_font_t *)cx->fonts.head;
t.best_score = 99999999;
if (t.hints)
lws_dll2_foreach_safe(&cx->fonts, &t, lws_fonts_score);
return t.best;
}

View File

@ -0,0 +1,890 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object handling
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
#define dlodump_loglevel LLL_NOTICE
#if (_LWS_ENABLED_LOGS & dlodump_loglevel)
#define lwsl_dlodump(...) _lws_log(dlodump_loglevel, __VA_ARGS__)
#else
#define lwsl_dlodump(...)
#endif
void
lws_display_dl_init(lws_displaylist_t *dl, lws_display_state_t *ds)
{
lws_dll2_owner_clear(&dl->dl);
dl->ds = ds;
}
int
lws_display_dlo_add(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, lws_dlo_t *dlo)
{
if (!dlo_parent && !dl->dl.head) {
lws_dll2_add_tail(&dlo->list, &dl->dl);
return 0;
}
if (!dlo_parent) {
if (!dl->dl.head)
return 0;
dlo_parent = lws_container_of(dl->dl.head, lws_dlo_t, list);
}
lws_dll2_add_tail(&dlo->list, &dlo_parent->children);
return 0;
}
void
lws_surface_set_px(const lws_surface_info_t *ic, uint8_t *line, int x,
const lws_display_colour_t *c)
{
unsigned int alpha, ialpha;
lws_display_colour_t oc;
lws_display_colour_t y;
uint8_t rgb[3];
if (x < 0 || x >= ic->wh_px[0].whole)
return;
/*
* All alpha composition takes place at 8bpp grey or 24bpp
*/
if (ic->greyscale) {
/* line composition buffer is 8-bit Y per pixel */
oc = line[x];
alpha = LWSDC_ALPHA(*c);
ialpha = 255 - alpha;
y = RGB_TO_Y(LWSDC_R(*c), LWSDC_G(*c), LWSDC_B(*c));
line[x] = (uint8_t)(((y * alpha) / 255) +
((LWSDC_R(oc) * ialpha) / 255));
return;
}
/* line composition buffer is 24-bit RGB per pixel */
line += (ic->render_to_rgba ? 4 : 3) * x;
alpha = LWSDC_ALPHA(*c);
ialpha = 255 - alpha;
rgb[0] = (uint8_t)(((LWSDC_R(*c) * alpha) / 255) +
((line[0] * ialpha) / 255));
rgb[1] = (uint8_t)(((LWSDC_G(*c) * alpha) / 255) +
((line[1] * ialpha) / 255));
rgb[2] = (uint8_t)(((LWSDC_B(*c) * alpha) / 255) +
((line[2] * ialpha) / 255));
*line++ = rgb[0];
*line++ = rgb[1];
*line++ = rgb[2];
if (ic->render_to_rgba)
*line = 0xff;
}
/*
* Recursively find out the total width and height of the contents of a DLO
*/
void
lws_dlo_contents(lws_dlo_t *parent, lws_dlo_dim_t *dim)
{
lws_display_render_stack_t st[12]; /* DLO child stack */
lws_dll2_t *d;
lws_fx_t t1;
int sp = 0;
dim->w.whole = 0;
dim->w.frac = 0;
dim->h.whole = 0;
dim->h.frac = 0;
if (!parent)
return;
d = lws_dll2_get_head(&parent->children);
if (!d)
return;
memset(&st, 0, sizeof(st));
st[0].dlo = lws_container_of(d, lws_dlo_t, list);
st[0].co.w.whole = 0;
st[0].co.w.frac = 0;
st[0].co.h.whole = 0;
st[0].co.h.frac = 0;
/* We are collecting worst dlo->box.x + dlo->box.w and .y + .h */
while (sp || st[0].dlo) {
lws_dlo_t *dlo = st[sp].dlo;
if (!dlo) {
if (!sp) {
lwsl_err("%s: underflow\n", __func__);
return;
}
if (lws_fx_comp(&st[sp].co.w, &st[sp - 1].co.w) > 0)
st[sp - 1].co.w = st[sp].co.w;
if (lws_fx_comp(&st[sp].co.h, &st[sp - 1].co.h) > 0)
st[sp - 1].co.h = st[sp].co.h;
// lwsl_notice("sp %d: passing back w: %d, h: %d\n", sp, st[sp - 1].co.w.whole, st[sp - 1].co.h.whole);
sp--;
continue;
}
lws_fx_add(&t1, &dlo->box.w, &dlo->box.x);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_LEFT]);
lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_LEFT]);
// lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_RIGHT]);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_RIGHT]);
if (lws_fx_comp(&t1, &st[sp].co.w) > 0)
st[sp].co.w = t1;
lws_fx_add(&t1, &dlo->box.h, &dlo->box.y);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_TOP]);
lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_TOP]);
// lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_BOTTOM]);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_BOTTOM]);
if (lws_fx_comp(&t1, &st[sp].co.h) > 0)
st[sp].co.h = t1;
d = dlo->list.next;
if (d)
st[sp].dlo = lws_container_of(d, lws_dlo_t, list);
else
st[sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (++sp == LWS_ARRAY_SIZE(st)) {
lwsl_err("%s: DLO stack overflow\n", __func__);
return;
}
st[sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
st[sp].co.w.whole = 0;
st[sp].co.h.whole = 0;
st[sp].co.w.frac = 0;
st[sp].co.h.frac = 0;
}
}
dim->w = st[0].co.w;
dim->h = st[0].co.h;
if (parent->col_list.owner) {
lhp_table_col_t *tc = lws_container_of(parent->col_list.owner,
lhp_table_col_t, col_dlos);
if (lws_fx_comp(&dim->w, &tc->width) < 0) {
// lws_fx_add(&t1, &tc->width, &parent->padding[CCPAS_LEFT]);
// lws_fx_add(&dim->w, &tc->width, &parent->padding[CCPAS_RIGHT]);
dim->w = tc->width;
}
}
if (parent->row_list.owner) {
lhp_table_row_t *tr = lws_container_of(parent->row_list.owner,
lhp_table_row_t, row_dlos);
if (lws_fx_comp(&dim->h, &tr->height) < 0) {
// lws_fx_add(&t1, &tr->height, &parent->padding[CCPAS_TOP]);
lws_fx_add(&dim->h, &tr->height, &parent->padding[CCPAS_BOTTOM]);
// dim->h = tr->height;
}
}
/*
lwsl_user("%s: dlo %p: FINAL w:%d -> %d h:%d -> %d\n", __func__, parent,
parent->box.w.whole, dim->w.whole,
parent->box.h.whole, dim->h.whole);
*/
}
/*
* Some DLO is changing height, adjust its height, and that of everybody below.
*/
void
lws_display_dlo_adjust_dims(lws_dlo_t *dlo, lws_dlo_dim_t *dim)
{
lws_dlo_dim_t delta;
if (!dim->w.whole && !dim->h.whole)
return;
/* adjust the target's width / height */
lws_fx_sub(&delta.w, &dim->w, &dlo->box.w);
lws_fx_sub(&delta.h, &dim->h, &dlo->box.h);
dlo->box.w = dim->w;
dlo->box.h = dim->h;
// lwsl_notice("%s: dlo %p: delta w:%d h:%d\n", __func__, dlo, delta.w.whole, delta.h.whole);
/* move peers below him accordingly */
do {
lws_dlo_t *dp = lws_container_of(dlo->list.owner, lws_dlo_t, children);
if (!dlo->list.owner)
break;
/*
* Adjust y pos of siblings below us
*/
do {
dlo = lws_container_of(dlo->list.next, lws_dlo_t, list);
if (dlo) {
//lwsl_notice("%s: dlo %p: adj y %d -> %d\n", __func__, dlo, dlo->box.y.whole, dlo->box.y.whole + delta.h.whole);
lws_fx_add(&dlo->box.y, &dlo->box.y, &delta.h);
}
} while (dlo);
/* go up parent chain until toplevel adjusting height of
* parent siblings below parent */
if (dp->flag_toplevel)
break;
dlo = dp;
//lwsl_notice("%s: dlo %p: adj h by %d\n", __func__, dlo, delta.h.whole);
lws_fx_add(&dlo->box.h, &dlo->box.h, &delta.h);
} while (1);
}
//#if defined(_DEBUG)
void
lws_display_dl_dump(lws_displaylist_t *dl)
{
lws_display_render_stack_t st[12]; /* DLO child stack */
int sp = 0;
lws_dll2_t *d = lws_dll2_get_head(&dl->dl);
#if (_LWS_ENABLED_LOGS & dlodump_loglevel)
static const char * const ind = " ";
#endif
char b[4][22], b1[4][22], dt[96];
if (!d) {
lwsl_notice("%s: empty dl\n", __func__);
return;
}
lwsl_notice("%s\n", __func__);
memset(&st, 0, sizeof(st));
st[0].dlo = lws_container_of(d, lws_dlo_t, list);
while (sp || st[0].dlo) {
lws_dlo_t *dlo = st[sp].dlo;
lws_box_t co;
//lws_fx_t t2;
if (!dlo) {
if (!sp) {
lwsl_err("%s: underflow\n", __func__);
return;
}
sp--;
continue;
}
lws_fx_add(&co.x, &st[sp].co.x, &dlo->box.x);
lws_fx_add(&co.y, &st[sp].co.y, &dlo->box.y);
co.w = dlo->box.w;
co.h = dlo->box.h;
lws_snprintf(dt, sizeof(dt), "rect: RGBA 0x%08X", (unsigned int)dlo->dc);
if (dlo->_destroy == lws_display_dlo_text_destroy) {
lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
lws_snprintf(dt, sizeof(dt), "text: RGBA 0x%08X, chars: %u, %.*s",
(unsigned int)dlo->dc, (unsigned int)text->text_len,
(int)text->text_len, text->text ? text->text : "(empty)");
}
#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_UPNG) && defined(LWS_WITH_CLIENT)
else if (dlo->_destroy == lws_display_dlo_png_destroy)
lws_snprintf(dt, sizeof(dt), "png");
#endif
#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_JPEG) && defined(LWS_WITH_CLIENT)
else if (dlo->_destroy == lws_display_dlo_jpeg_destroy)
lws_snprintf(dt, sizeof(dt), "jpeg");
#endif
lws_fx_string(&dlo->box.x, b[0], sizeof(b[0]));
lws_fx_string(&dlo->box.y, b[1], sizeof(b[1]));
lws_fx_string(&dlo->box.w, b[2], sizeof(b[2]));
lws_fx_string(&dlo->box.h, b[3], sizeof(b[3]));
lws_fx_string(&co.x, b1[0], sizeof(b1[0]));
lws_fx_string(&co.y, b1[1], sizeof(b1[1]));
lws_fx_string(&co.w, b1[2], sizeof(b1[2]));
lws_fx_string(&co.h, b1[3], sizeof(b1[3]));
lwsl_dlodump("%.*s %p box: (%s, %s) [%s x %s], co: (%s, %s) [%s x %s], %s\n",
sp, ind, dlo, b[0], b[1], b[2], b[3],
b1[0], b1[1], b1[2], b1[3], dt);
d = dlo->list.next;
if (d)
st[sp].dlo = lws_container_of(d, lws_dlo_t, list);
else
st[sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (sp + 1 == LWS_ARRAY_SIZE(st)) {
lwsl_err("%s: DLO stack overflow\n", __func__);
return;
}
st[++sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
st[sp].co = co;
}
}
}
//#endif
/*
* Go through every DLO once, setting its id->box to the final layout for the
* related dlo, if any
*/
lws_stateful_ret_t
lws_display_get_ids_boxes(lws_display_render_state_t *rs)
{
lws_dll2_t *d;
rs->lowest_id_y = 0;
d = lws_dll2_get_head(&rs->displaylist.dl);
if (!d)
/* nothing in dlo */
return LWS_SRET_OK;
memset(&rs->st[0].co, 0, sizeof(rs->st[0].co));
rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list);
while (rs->sp || rs->st[0].dlo) {
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_box_t co;
lws_fx_t t2;
if (!dlo) {
rs->sp--;
continue;
}
lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y);
co.w = dlo->box.w;
co.h = dlo->box.h;
lws_fx_add(&t2, &co.y, &dlo->box.h);
if (dlo->id) {
lws_display_id_t *id = dlo->id;
lwsl_debug("%s: set id box %s\n", __func__, id->id);
id->box = co;
dlo->id = NULL; /* decouple us */
}
if (co.y.whole + co.h.whole > rs->lowest_id_y) {
rs->lowest_id_y = (lws_display_scalar)(co.y.whole + co.h.whole);
if (rs->lowest_id_y > rs->ic->wh_px[1].whole)
rs->lowest_id_y = (lws_display_scalar)rs->ic->wh_px[1].whole;
}
/* next sibling at this level if any */
d = dlo->list.next;
if (d)
rs->st[rs->sp].dlo = lws_container_of(d,
lws_dlo_t, list);
else
rs->st[rs->sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) {
lwsl_err("%s: DLO stack overflow\n",
__func__);
return LWS_SRET_FATAL;
}
rs->st[++rs->sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
rs->st[rs->sp].co = co;
continue;
}
}
lws_display_render_dump_ids(&rs->ids);
return LWS_SRET_OK;
}
lws_stateful_ret_t
lws_display_list_render_line(lws_display_render_state_t *rs)
{
lws_dll2_t *d;
if (rs->html == 1)
return LWS_SRET_WANT_INPUT;
if (!rs->sp && !rs->st[0].dlo) {
/* starting a line */
d = lws_dll2_get_head(&rs->displaylist.dl);
if (!d)
/* nothing in dlo */
return LWS_SRET_OK;
// memset(rs->line, 0, (size_t)rs->ic->wh_px[0].whole *
// (rs->ic->greyscale ? 1 : 3));
memset(&rs->st[0].co, 0, sizeof(rs->st[0].co));
rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list);
}
while (rs->sp || rs->st[0].dlo) {
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_stateful_ret_t r;
lws_box_t co;
lws_fx_t t2;
if (!dlo) {
rs->sp--;
continue;
}
// lwsl_notice("%s: curr %d: %d %d %d %d\n", __func__, (int)rs->curr, (int)dlo->box.x.whole, (int)dlo->box.y.whole, (int)dlo->box.w.whole, (int)dlo->box.h.whole);
lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y);
co.w = dlo->box.w;
co.h = dlo->box.h;
lws_fx_add(&t2, &co.y, &dlo->box.h);
if (rs->curr > lws_fx_roundup(&t2)) {
d = dlo->list.next;
rs->st[rs->sp].dlo = d ? lws_container_of(d, lws_dlo_t,
list) : NULL;
lws_display_dlo_destroy(&dlo);
continue;
}
#if 0
if (dlo->_destroy == lws_display_dlo_png_destroy)
lwsl_err("png line %d %d %d %d\n", rs->curr, co.y.whole - 1,
rs->st[rs->sp].co.y.whole, dlo->box.y.whole);
#endif
if (rs->curr >= co.y.whole - 1) {
r = dlo->render(rs);
//rs->ic, dlo, &rs->st[rs->sp].co,
// rs->curr, rs->line, &dlo->nle[0]);
if (r)
return r;
/* next sibling at this level if any */
d = dlo->list.next;
if (d)
rs->st[rs->sp].dlo = lws_container_of(d,
lws_dlo_t, list);
else
rs->st[rs->sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) {
lwsl_err("%s: DLO stack overflow\n",
__func__);
return LWS_SRET_FATAL;
}
rs->st[++rs->sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
rs->st[rs->sp].co = co;
continue;
}
} else {
/* next sibling at this level if any */
d = dlo->list.next;
if (d)
rs->st[rs->sp].dlo = lws_container_of(d,
lws_dlo_t, list);
else
rs->st[rs->sp].dlo = NULL;
}
}
return LWS_SRET_OK;
}
static int
dlo_clean_table_rows(lws_dll2_t *d, void *user)
{
lhp_table_row_t *r = lws_container_of(d, lhp_table_row_t, list);
lws_dll2_remove(d);
lws_free(r);
return 0;
}
static int
dlo_clean_table_cols(lws_dll2_t *d, void *user)
{
lhp_table_col_t *c = lws_container_of(d, lhp_table_col_t, list);
lws_dll2_remove(d);
lws_free(c);
return 0;
}
void
lws_display_dlo_destroy(lws_dlo_t **r)
{
if (!(*r))
return;
lws_dll2_remove(&(*r)->list);
lws_dll2_remove(&(*r)->col_list);
lws_dll2_remove(&(*r)->row_list);
while ((*r)->children.head) {
lws_dlo_t *d = lws_container_of((*r)->children.head,
lws_dlo_t, list);
lws_display_dlo_destroy(&d);
}
lws_dll2_foreach_safe(&(*r)->table_cols, NULL, dlo_clean_table_cols);
lws_dll2_foreach_safe(&(*r)->table_rows, NULL, dlo_clean_table_rows);
if ((*r)->_destroy)
(*r)->_destroy(*r);
lws_free_set_NULL(*r);
*r = NULL;
}
void
lws_display_list_destroy(lws_displaylist_t *dl)
{
if (!dl)
return;
while (dl->dl.head) {
lws_dlo_t *d = lws_container_of(dl->dl.head, lws_dlo_t, list);
lws_display_dlo_destroy(&d);
}
}
lws_dlo_filesystem_t *
lws_dlo_file_register(struct lws_context *cx, const lws_dlo_filesystem_t *f)
{
const lws_dlo_filesystem_t *b;
lws_dlo_filesystem_t *a;
b = lws_dlo_file_choose(cx, f->name);
if (b) {
lwsl_err("%s: dlo file %s already exists %p\n", __func__, b->name, b);
lws_dlo_file_unregister((lws_dlo_filesystem_t **)&b);
}
a = lws_malloc(sizeof(*a), __func__);
if (!a)
return NULL;
*a = *f;
lws_dll2_clear(&a->list);
lws_dll2_add_tail(&a->list, &cx->dlo_file);
lwsl_err("%s: dlo file %s registered at %p\n", __func__, a->name, a);
return a;
}
/*
* Only needed with heap-alloc'd lws_dlo_filesystem_t
*/
void
lws_dlo_file_unregister(lws_dlo_filesystem_t **f)
{
if (!*f)
return;
lws_dll2_remove(&(*f)->list);
lws_free_set_NULL(*f);
}
void
lws_dlo_file_unregister_by_name(struct lws_context *cx, const char *name)
{
lws_dlo_filesystem_t *a;
a = (lws_dlo_filesystem_t *)lws_dlo_file_choose(cx, name);
if (!a)
return;
lws_dll2_remove(&a->list);
lws_free_set_NULL(a);
}
static int
_lws_dlo_file_destroy(struct lws_dll2 *d, void *user)
{
lws_free(d);
return 0;
}
void
lws_dlo_file_destroy(struct lws_context *cx)
{
lws_dll2_foreach_safe(&cx->dlo_file, NULL, _lws_dlo_file_destroy);
}
const lws_dlo_filesystem_t *
lws_dlo_file_choose(struct lws_context *cx, const char *name)
{
lws_start_foreach_dll(struct lws_dll2 *, p,
lws_dll2_get_head(&cx->dlo_file)) {
const lws_dlo_filesystem_t *pn = lws_container_of(p,
lws_dlo_filesystem_t, list);
if (!strcmp(name, pn->name))
return pn;
} lws_end_foreach_dll(p);
return NULL;
}
static int
lws_display_id_destroy(struct lws_dll2 *d, void *user)
{
lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list);
lws_dll2_remove(&id->list);
lws_free(id);
return 0;
}
void
lws_display_render_free_ids(lws_display_render_state_t *rs)
{
lws_dll2_foreach_safe(&rs->ids, NULL, lws_display_id_destroy);
}
lws_display_id_t *
lws_display_render_get_id(lws_display_render_state_t *rs, const char *_id)
{
lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&rs->ids)) {
lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list);
if (!strcmp(_id, id->id))
return id;
} lws_end_foreach_dll(d);
return NULL;
}
lws_display_id_t *
lws_display_render_add_id(lws_display_render_state_t *rs, const char *_id, void *priv)
{
lws_display_id_t *id;
id = lws_display_render_get_id(rs, _id);
if (id) {
id->priv_user = priv;
return id;
}
id = lws_zalloc(sizeof(*id), __func__);
if (id) {
lws_strncpy(id->id, _id, sizeof(id->id));
id->priv_user = priv;
lws_dll2_add_tail(&id->list, &rs->ids);
}
return id;
}
void
lws_display_render_dump_ids(lws_dll2_owner_t *ids)
{
lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(ids)) {
lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list);
if (!id->exists)
lwsl_notice(" id: '%s' (not present)\n", id->id);
else
lwsl_notice(" id: '%s', (%d,%d), %dx%d\n", id->id,
(int)id->box.x.whole, (int)id->box.y.whole,
(int)id->box.w.whole, (int)id->box.h.whole);
} lws_end_foreach_dll(d);
}
#if defined (LWS_WITH_FILE_OPS)
int
dlo_filesystem_fops_close(lws_fop_fd_t *fop_fd)
{
lws_free_set_NULL(*fop_fd);
return 0;
}
lws_fileofs_t
dlo_filesystem_fops_seek_cur(lws_fop_fd_t fop_fd,
lws_fileofs_t pos)
{
if (pos < 0)
fop_fd->pos = 0;
else
if (pos >= (long long)fop_fd->len)
fop_fd->pos = fop_fd->len;
else
fop_fd->pos = (lws_filepos_t)pos;
return (lws_fileofs_t)fop_fd->pos;
}
int
dlo_filesystem_fops_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
*amount = 0;
return -1;
}
int
dlo_filesystem_fops_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
const uint8_t *p = (uint8_t *)fop_fd->filesystem_priv;
lws_filepos_t amt = *amount;
*amount = 0;
if (fop_fd->len <= fop_fd->pos)
return 0;
if (amt > fop_fd->len - fop_fd->pos)
amt = fop_fd->len - fop_fd->pos;
if (amt > len)
amt = len;
memcpy(buf, p + fop_fd->pos, (size_t)amt);
fop_fd->pos += amt;
*amount = amt;
return 0;
}
lws_fop_fd_t
lws_dlo_filesystem_fops_open(const struct lws_plat_file_ops *fops_own,
const struct lws_plat_file_ops *fops,
const char *vfs_path, const char *vpath,
lws_fop_flags_t *flags)
{
const lws_dlo_filesystem_t *f = NULL;
lws_fop_fd_t fop_fd;
// lwsl_err("%s: %s\n", __func__, vpath);
f = lws_dlo_file_choose(fops->cx, vpath);
if (f) {
/* we will handle it then */
fop_fd = lws_zalloc(sizeof(*fop_fd), __func__);
if (!fop_fd)
return NULL;
fop_fd->fops = fops_own;
fop_fd->filesystem_priv = (void *)f->data;
fop_fd->pos = 0;
fop_fd->len = f->len;
// lwsl_notice("%s: Opened %s\n", __func__, vpath);
return fop_fd;
} else
lwsl_err("%s: failed to open %s\n", __func__, vpath);
return NULL;
}
const struct lws_plat_file_ops lws_dlo_fops = {
.LWS_FOP_OPEN = lws_dlo_filesystem_fops_open,
.LWS_FOP_CLOSE = dlo_filesystem_fops_close,
.LWS_FOP_SEEK_CUR = dlo_filesystem_fops_seek_cur,
.LWS_FOP_READ = dlo_filesystem_fops_read,
.LWS_FOP_WRITE = dlo_filesystem_fops_write,
.fi = { { "dlofs/", 6 } },
};
#endif

View File

@ -0,0 +1,57 @@
enum {
MCUFO_MAGIC = 0,
MCUFO_FLAGS_VER = 4,
MCUFO_FOFS_FULLNAME = 8,
MCUFO_FOFS_NAME = 0xc,
MCUFO_FOFS_DICT_DATA = 0x10,
MCUFO_SIZE_DICT_DATA = 0x14,
MCUFO_FOFS_DICT_OFS = 0x18,
MCUFO_COUNT_RLE_DICT = 0x1C,
MCUFO_COUNT_REF_RLE_DICT = 0x20,
MCUFO_FOFS_CHAR_RANGE_TABLES = 0x24,
MCUFO_COUNT_CHAR_RANGE_TABLES = 0x28,
MCUFO_UNICODE_FALLBACK = 0x2C,
MCUFO16_WIDTH = 0x30,
MCUFO16_HEIGHT = 0x32,
MCUFO16_MIN_X_ADV = 0x34,
MCUFO16_MAX_X_ADV = 0x36,
MCUFO16_BASELINE_X = 0x38,
MCUFO16_BASELINE_Y = 0x3a,
MCUFO16_LINE_HEIGHT = 0x3c,
};
void
dist_err_floyd_steinberg_grey(int n, int width, lws_greyscale_error_t *gedl_this,
lws_greyscale_error_t *gedl_next);
void
dist_err_floyd_steinberg_col(int n, int width, lws_colour_error_t *edl_this,
lws_colour_error_t *edl_next);
int
lws_display_alloc_diffusion(const lws_surface_info_t *ic, lws_surface_error_t **se);
size_t
utf8_bytes(uint8_t u);
int
lws_display_font_mcufont_getcwidth(lws_dlo_text_t *text, uint32_t unicode,
lws_fx_t *fx);
int
lws_display_dlo_text_attach_glyphs(lws_dlo_text_t *text);
lws_stateful_ret_t
lws_display_font_mcufont_render(struct lws_display_render_state *rs);
lws_font_glyph_t *
lws_display_font_mcufont_image_glyph(lws_dlo_text_t *text, uint32_t unicode,
char attach);
void
lws_lhp_ss_html_parse_from_lhp(lhp_ctx_t *lhp);
void
lws_lhp_image_dimensions_cb(lws_sorted_usec_list_t *sul);