Layout-Flex
view release on metacpan or search on metacpan
PUTBACK;
FREETMPS; LEAVE;
}
/* ââ Font::Metrics measure helpers (direct C calls, no Perl dispatch) ââ */
static void
lf_measure_fm(const fm_font_t *fm, const char *text, int is_utf8, float size, float line_h,
float *w, float *h)
{
*w = is_utf8 ? fm_string_width_utf8(fm, text, size) : fm_string_width(fm, text, size);
*h = line_h;
}
static void
lf_measure_fm_wrapped(const fm_font_t *fm, const char *text, int is_utf8, float size,
float line_h, float avail_w, float *w, float *h)
{
size_t tlen = strlen(text);
char *buf = (char *)malloc(tlen + 2);
const char *p, *ws; size_t wlen, llen; int lines; float cw;
buf[0] = '\0'; llen = 0; lines = 0;
p = text;
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
while (*p) {
ws = p;
while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') p++;
wlen = (size_t)(p - ws);
if (llen > 0) {
buf[llen] = ' ';
memcpy(buf + llen + 1, ws, wlen);
buf[llen + 1 + wlen] = '\0';
cw = is_utf8 ? fm_string_width_utf8(fm, buf, size)
: fm_string_width(fm, buf, size);
if (cw > avail_w) {
lines++;
memcpy(buf, ws, wlen);
buf[wlen] = '\0'; llen = wlen;
} else { llen += 1 + wlen; }
} else {
memcpy(buf, ws, wlen);
buf[wlen] = '\0'; llen = wlen; lines = 1;
}
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
}
free(buf);
*w = avail_w;
*h = lines > 0 ? (float)lines * line_h : 0.0f;
}
MODULE = Layout::Flex PACKAGE = Layout::Flex
PROTOTYPES: DISABLE
BOOT:
fm_std14_init();
void
compute(class, ...)
SV * class
PPCODE:
{
lf_ctx_t ctx;
lf_item_t *lf_items = NULL;
lf_rect_t *rects = NULL;
AV *items_av = NULL;
SV *measure_sv = NULL;
SV *fm_measure_sv = NULL;
fm_font_t *fm_ptr = NULL;
float fm_size = 12.0f, fm_line_h = 14.4f;
int i, n;
PERL_UNUSED_VAR(class);
/* ââ defaults ââ */
ctx.main_size = 0.0f;
ctx.cross_size = 0.0f;
ctx.direction = LF_DIR_ROW;
ctx.justify = LF_JUST_START;
ctx.align_items = LF_ALIGN_STRETCH;
ctx.wrap = LF_WRAP_NOWRAP;
ctx.align_content = LF_ACONT_STRETCH;
ctx.main_gap = 0.0f;
ctx.cross_gap = 0.0f;
ctx.items = NULL;
ctx.n_items = 0;
/* ââ parse named args ââ */
for (i = 1; i + 1 < items; i += 2) {
const char *key = SvPV_nolen(ST(i));
SV *val = ST(i + 1);
if (strEQ(key, "main_size")) {
ctx.main_size = (float)SvNV(val);
} else if (strEQ(key, "cross_size")) {
ctx.cross_size = (float)SvNV(val);
} else if (strEQ(key, "direction")) {
const char *d = SvPV_nolen(val);
ctx.direction = strEQ(d, "column") ? LF_DIR_COLUMN : LF_DIR_ROW;
} else if (strEQ(key, "justify")) {
const char *j = SvPV_nolen(val);
if (strEQ(j, "end")) ctx.justify = LF_JUST_END;
else if (strEQ(j, "center")) ctx.justify = LF_JUST_CENTER;
else if (strEQ(j, "space-between")) ctx.justify = LF_JUST_SPACE_BETWEEN;
else if (strEQ(j, "space-around")) ctx.justify = LF_JUST_SPACE_AROUND;
else if (strEQ(j, "space-evenly")) ctx.justify = LF_JUST_SPACE_EVENLY;
else ctx.justify = LF_JUST_START;
} else if (strEQ(key, "align")) {
int a = lf_parse_align(SvPV_nolen(val));
ctx.align_items = (a >= 0) ? a : LF_ALIGN_STRETCH;
} else if (strEQ(key, "wrap")) {
const char *w = SvPV_nolen(val);
if (strEQ(w, "wrap")) ctx.wrap = LF_WRAP_WRAP;
else if (strEQ(w, "wrap-reverse")) ctx.wrap = LF_WRAP_REVERSE;
else ctx.wrap = LF_WRAP_NOWRAP;
} else if (strEQ(key, "align_content")) {
const char *ac = SvPV_nolen(val);
if (strEQ(ac, "start")) ctx.align_content = LF_ACONT_START;
else if (strEQ(ac, "end")) ctx.align_content = LF_ACONT_END;
else if (strEQ(ac, "center")) ctx.align_content = LF_ACONT_CENTER;
else if (strEQ(ac, "space-between")) ctx.align_content = LF_ACONT_SPACE_BETWEEN;
if (measure_sv || fm_measure_sv) {
int needs_recompute = 0;
for (i = 0; i < n; i++) {
SV **elem2 = av_fetch(items_av, (SSize_t)i, 0);
HV *hv2;
SV **wt_sv, **text_sv2;
float avail, tw, th, new_cross;
if (!elem2 || !SvOK(*elem2) || !SvROK(*elem2)
|| SvTYPE(SvRV(*elem2)) != SVt_PVHV) continue;
hv2 = (HV *)SvRV(*elem2);
wt_sv = hv_fetch(hv2, "wrap_text", 9, 0);
if (!wt_sv || !SvOK(*wt_sv) || !SvTRUE(*wt_sv)) continue;
text_sv2 = hv_fetch(hv2, "text", 4, 0);
if (!text_sv2 || !SvOK(*text_sv2)) continue;
avail = (ctx.direction == LF_DIR_ROW) ? rects[i].w : rects[i].h;
tw = 0.0f; th = 0.0f;
if (fm_measure_sv) {
lf_measure_fm_wrapped(fm_ptr, SvPV_nolen(*text_sv2),
SvUTF8(*text_sv2),
fm_size, fm_line_h, avail, &tw, &th);
} else if (SvROK(measure_sv)) {
lf_measure_cb_wrapped(aTHX_ measure_sv, hv2, avail, &tw, &th);
} else {
float font_size = 12.0f;
lf_hv_float(aTHX_ hv2, "font_size", 9, &font_size);
lf_measure_simple_wrapped(SvPV_nolen(*text_sv2),
font_size, avail, &tw, &th);
}
new_cross = (ctx.direction == LF_DIR_ROW) ? th : tw;
if (new_cross != lf_items[i].cross) {
lf_items[i].cross = new_cross;
needs_recompute = 1;
}
}
if (needs_recompute) lf_compute(&ctx, rects);
}
EXTEND(SP, n);
for (i = 0; i < n; i++) {
AV *rect = newAV();
av_push(rect, newSVnv((NV)rects[i].x));
av_push(rect, newSVnv((NV)rects[i].y));
av_push(rect, newSVnv((NV)rects[i].w));
av_push(rect, newSVnv((NV)rects[i].h));
PUSHs(sv_2mortal(newRV_noinc((SV *)rect)));
}
free(lf_items);
free(rects);
}
void
font_metrics_measure(class, ...)
SV *class
PPCODE:
{
SV *fm_sv = NULL;
NV size = 12.0;
NV line_h = 0.0;
HV *hv;
SV *obj;
int i;
PERL_UNUSED_VAR(class);
for (i = 1; i + 1 < items; i += 2) {
const char *k = SvPV_nolen(ST(i));
SV *v = ST(i + 1);
if (strEQ(k, "fm")) { fm_sv = v; }
else if (strEQ(k, "size")) { size = SvNV(v); }
else if (strEQ(k, "line_height")) { line_h = SvNV(v); }
}
if (!fm_sv || !SvOK(fm_sv) || !SvROK(fm_sv))
croak("font_metrics_measure: fm must be a Font::Metrics object");
if (line_h <= 0.0) line_h = size * 1.2;
hv = newHV();
hv_store(hv, "fm", 2, SvREFCNT_inc(fm_sv), 0);
hv_store(hv, "fmp", 3, newSVuv(PTR2UV(INT2PTR(fm_font_t *,
SvIV(SvRV(fm_sv))))), 0);
hv_store(hv, "size", 4, newSVnv(size), 0);
hv_store(hv, "line_h", 6, newSVnv(line_h), 0);
obj = sv_bless(newRV_noinc((SV *)hv),
gv_stashpv("Layout::Flex::FMeasure", GV_ADD));
EXTEND(SP, 1);
PUSHs(sv_2mortal(obj));
XSRETURN(1);
}
( run in 1.281 second using v1.01-cache-2.11-cpan-4e7a2411597 )