801 lines
28 KiB
C
801 lines
28 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||
│ │
|
||
│ This program is free software; you can redistribute it and/or modify │
|
||
│ it under the terms of the GNU General Public License as published by │
|
||
│ the Free Software Foundation; version 2 of the License. │
|
||
│ │
|
||
│ This program is distributed in the hope that it will be useful, but │
|
||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||
│ General Public License for more details. │
|
||
│ │
|
||
│ You should have received a copy of the GNU General Public License │
|
||
│ along with this program; if not, write to the Free Software │
|
||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||
│ 02110-1301 USA │
|
||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
#include "dsp/core/twixt8.h"
|
||
#include "dsp/tty/quant.h"
|
||
#include "dsp/tty/tty.h"
|
||
#include "dsp/tty/ttyrgb.h"
|
||
#include "dsp/tty/windex.h"
|
||
#include "libc/assert.h"
|
||
#include "libc/bits/bits.h"
|
||
#include "libc/bits/safemacros.internal.h"
|
||
#include "libc/limits.h"
|
||
#include "libc/log/check.h"
|
||
#include "libc/log/log.h"
|
||
#include "libc/macros.h"
|
||
#include "libc/math.h"
|
||
#include "libc/nexgen32e/x86feature.h"
|
||
#include "libc/runtime/runtime.h"
|
||
#include "libc/stdio/stdio.h"
|
||
#include "libc/str/str.h"
|
||
|
||
#define SQR(X) ((X) * (X))
|
||
#define DIST(X, Y) ((X) - (Y))
|
||
#define QSUB(A, B, C) ABS(DIST(A##C, B##C))
|
||
#define DIFF(A, B) (QSUB(A, B, r) + QSUB(A, B, g) + QSUB(A, B, b))
|
||
#define JUDGE(M) (DIFF(ttl, M) + DIFF(ttr, M) + DIFF(tbl, M) + DIFF(tbr, M))
|
||
|
||
static const struct Glyph {
|
||
char c1, c2, c3, len;
|
||
} kGlyphs[2][11] = {
|
||
{{0x20, 0x00, 0x00, 1}, /* */
|
||
{0xE2, 0x96, 0x84, 3}, /* ▄ */
|
||
{0xE2, 0x96, 0x8C, 3}, /* ▌ */
|
||
{0xE2, 0x96, 0x9D, 3}, /* ▝ */
|
||
{0xE2, 0x96, 0x97, 3}, /* ▗ */
|
||
{0xE2, 0x96, 0x96, 3}, /* ▖ */
|
||
{0xE2, 0x96, 0x9E, 3}, /* ▞ */
|
||
{0xE2, 0x96, 0x98, 3}, /* ▘ */
|
||
{0xE2, 0x96, 0x91, 3}, /* ░ */
|
||
{0xE2, 0x96, 0x93, 3}, /* ▓ */
|
||
{0xE2, 0x96, 0x92, 3}}, /* ▒ */
|
||
{{0xE2, 0x96, 0x88, 3}, /* █ */
|
||
{0xE2, 0x96, 0x80, 3}, /* ▀ */
|
||
{0xE2, 0x96, 0x90, 3}, /* ▐ */
|
||
{0xE2, 0x96, 0x99, 3}, /* ▙ */
|
||
{0xE2, 0x96, 0x9B, 3}, /* ▛ */
|
||
{0xE2, 0x96, 0x9C, 3}, /* ▜ */
|
||
{0xE2, 0x96, 0x9A, 3}, /* ▚ */
|
||
{0xE2, 0x96, 0x9F, 3}, /* ▟ */
|
||
{0xE2, 0x96, 0x93, 3}, /* ▓ */
|
||
{0xE2, 0x96, 0x91, 3}, /* ░ */
|
||
{0xE2, 0x96, 0x92, 3}}, /* ▒ */
|
||
};
|
||
|
||
static const struct Pick {
|
||
unsigned char fg, bg, k;
|
||
} kPicksUnicode[96] = {
|
||
{-1, BL, 0}, /* */
|
||
{BR, BL, 4}, /* ▗ */
|
||
{TL, BL, 4}, /* ▗ */
|
||
{TR, BL, 4}, /* ▗ */
|
||
{BR, BL, 5}, /* ▖ */
|
||
{BR, BL, 1}, /* ▄ */
|
||
{TL, BL, 5}, /* ▖ */
|
||
{TL, BL, 1}, /* ▄ */
|
||
{TR, BL, 5}, /* ▖ */
|
||
{TR, BL, 1}, /* ▄ */
|
||
{BR, BL, 3}, /* ▝ */
|
||
{BL, BR, 2}, /* ▌ */
|
||
{BR, BL, 6}, /* ▞ */
|
||
{BL, BR, 7}, /* ▘ */
|
||
{TL, BL, 3}, /* ▝ */
|
||
{BL, TL, 2}, /* ▌ */
|
||
{TL, BL, 6}, /* ▞ */
|
||
{BL, TL, 7}, /* ▘ */
|
||
{TR, BL, 3}, /* ▝ */
|
||
{BL, TR, 2}, /* ▌ */
|
||
{TR, BL, 6}, /* ▞ */
|
||
{BL, TR, 7}, /* ▘ */
|
||
{BR, BL, 7}, /* ▘ */
|
||
{BL, BR, 6}, /* ▞ */
|
||
{BR, BL, 2}, /* ▌ */
|
||
{BL, BR, 3}, /* ▝ */
|
||
{BL, BR, 1}, /* ▄ */
|
||
{BL, BR, 5}, /* ▖ */
|
||
{BL, BR, 4}, /* ▗ */
|
||
{-1, BR, 0}, /* */
|
||
{TL, BR, 4}, /* ▗ */
|
||
{TR, BR, 4}, /* ▗ */
|
||
{TL, BR, 5}, /* ▖ */
|
||
{TL, BR, 1}, /* ▄ */
|
||
{TR, BR, 5}, /* ▖ */
|
||
{TR, BR, 1}, /* ▄ */
|
||
{TL, BR, 3}, /* ▝ */
|
||
{BR, TL, 2}, /* ▌ */
|
||
{TL, BR, 6}, /* ▞ */
|
||
{BR, TL, 7}, /* ▘ */
|
||
{TR, BR, 3}, /* ▝ */
|
||
{BR, TR, 2}, /* ▌ */
|
||
{TR, BR, 6}, /* ▞ */
|
||
{BR, TR, 7}, /* ▘ */
|
||
{TL, BL, 7}, /* ▘ */
|
||
{BL, TL, 6}, /* ▞ */
|
||
{TL, BL, 2}, /* ▌ */
|
||
{BL, TL, 3}, /* ▝ */
|
||
{TL, BR, 7}, /* ▘ */
|
||
{BR, TL, 6}, /* ▞ */
|
||
{TL, BR, 2}, /* ▌ */
|
||
{BR, TL, 3}, /* ▝ */
|
||
{BL, TL, 1}, /* ▄ */
|
||
{BL, TL, 5}, /* ▖ */
|
||
{BR, TL, 1}, /* ▄ */
|
||
{BR, TL, 5}, /* ▖ */
|
||
{BL, TL, 4}, /* ▗ */
|
||
{BR, TL, 4}, /* ▗ */
|
||
{-1, TL, 0}, /* */
|
||
{TR, TL, 4}, /* ▗ */
|
||
{TR, TL, 5}, /* ▖ */
|
||
{TR, TL, 1}, /* ▄ */
|
||
{TR, TL, 3}, /* ▝ */
|
||
{TL, TR, 2}, /* ▌ */
|
||
{TR, TL, 6}, /* ▞ */
|
||
{TL, TR, 7}, /* ▘ */
|
||
{TR, BL, 7}, /* ▘ */
|
||
{BL, TR, 6}, /* ▞ */
|
||
{TR, BL, 2}, /* ▌ */
|
||
{BL, TR, 3}, /* ▝ */
|
||
{TR, BR, 7}, /* ▘ */
|
||
{BR, TR, 6}, /* ▞ */
|
||
{TR, BR, 2}, /* ▌ */
|
||
{BR, TR, 3}, /* ▝ */
|
||
{TR, TL, 7}, /* ▘ */
|
||
{TL, TR, 6}, /* ▞ */
|
||
{TR, TL, 2}, /* ▌ */
|
||
{TL, TR, 3}, /* ▝ */
|
||
{BL, TR, 1}, /* ▄ */
|
||
{BL, TR, 5}, /* ▖ */
|
||
{BR, TR, 1}, /* ▄ */
|
||
{BR, TR, 5}, /* ▖ */
|
||
{TL, TR, 1}, /* ▄ */
|
||
{TL, TR, 5}, /* ▖ */
|
||
{BL, TR, 4}, /* ▗ */
|
||
{BR, TR, 4}, /* ▗ */
|
||
{TL, TR, 4}, /* ▗ */
|
||
{-1, TR, 0}, /* */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
};
|
||
|
||
static const struct Pick kPicksCp437[32] = {
|
||
{-1, BL, 0}, /* */
|
||
{BR, BL, 1}, /* ▄ */
|
||
{TL, BL, 1}, /* ▄ */
|
||
{TR, BL, 1}, /* ▄ */
|
||
{BL, BR, 1}, /* ▄ */
|
||
{BL, TL, 1}, /* ▄ */
|
||
{BL, TR, 1}, /* ▄ */
|
||
{BL, BR, 2}, /* ▌ */
|
||
{BL, TL, 2}, /* ▌ */
|
||
{BL, TR, 2}, /* ▌ */
|
||
{BR, BL, 2}, /* ▌ */
|
||
{-1, BR, 0}, /* */
|
||
{TL, BR, 1}, /* ▄ */
|
||
{TR, BR, 1}, /* ▄ */
|
||
{BR, TL, 2}, /* ▌ */
|
||
{BR, TR, 2}, /* ▌ */
|
||
{TL, BL, 2}, /* ▌ */
|
||
{TL, BR, 2}, /* ▌ */
|
||
{BR, TL, 1}, /* ▄ */
|
||
{-1, TL, 0}, /* */
|
||
{TR, TL, 1}, /* ▄ */
|
||
{TL, TR, 2}, /* ▌ */
|
||
{TR, BL, 2}, /* ▌ */
|
||
{TR, BR, 2}, /* ▌ */
|
||
{TR, TL, 2}, /* ▌ */
|
||
{BR, TR, 1}, /* ▄ */
|
||
{TL, TR, 1}, /* ▄ */
|
||
{-1, TR, 0}, /* */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
{-1, -1, -1}, /* X */
|
||
};
|
||
|
||
static const struct Pick kPicksMixBlock[32] = {
|
||
{BR, BL, 8}, /* ░ */
|
||
{BR, BL, 9}, /* ▓ */
|
||
{TL, BL, 8}, /* ░ */
|
||
{TL, BL, 9}, /* ▓ */
|
||
{TR, BL, 8}, /* ░ */
|
||
{TR, BL, 9}, /* ▓ */
|
||
{TL, BR, 8}, /* ░ */
|
||
{TL, BR, 9}, /* ▓ */
|
||
{TR, BR, 8}, /* ░ */
|
||
{TR, BR, 9}, /* ▓ */
|
||
{BL, TL, 8}, /* ░ */
|
||
{BL, TL, 9}, /* ▓ */
|
||
{BL, TL, 8}, /* ░ */
|
||
{BL, TL, 9}, /* ▓ */
|
||
{TR, TL, 8}, /* ░ */
|
||
{TR, TL, 9}, /* ▓ */
|
||
{BL, TR, 8}, /* ░ */
|
||
{BL, TR, 9}, /* ▓ */
|
||
{BR, TR, 8}, /* ░ */
|
||
{BR, TR, 9}, /* ▓ */
|
||
{TL, TR, 8}, /* ░ */
|
||
{TL, TR, 9}, /* ▓ */
|
||
/**/
|
||
{BL, TL, 8}, /* ░ */
|
||
{BL, TL, 9}, /* ▓ */
|
||
{TR, TL, 8}, /* ░ */
|
||
{TR, TL, 9}, /* ▓ */
|
||
{BL, TR, 8}, /* ░ */
|
||
{BL, TR, 9}, /* ▓ */
|
||
{BR, TR, 8}, /* ░ */
|
||
{BR, TR, 9}, /* ▓ */
|
||
{TL, TR, 8}, /* ░ */
|
||
{TL, TR, 9}, /* ▓ */
|
||
};
|
||
|
||
static unsigned short GetBlockDist(struct TtyRgb a, struct TtyRgb b,
|
||
struct TtyRgb c, struct TtyRgb d,
|
||
struct TtyRgb w, struct TtyRgb x,
|
||
struct TtyRgb y, struct TtyRgb z) {
|
||
unsigned short dist;
|
||
dist = 0;
|
||
dist += ABS(a.r - w.r);
|
||
dist += ABS(a.g - w.g);
|
||
dist += ABS(a.b - w.b);
|
||
dist += ABS(b.r - x.r);
|
||
dist += ABS(b.g - x.g);
|
||
dist += ABS(b.b - x.b);
|
||
dist += ABS(c.r - y.r);
|
||
dist += ABS(c.g - y.g);
|
||
dist += ABS(c.b - y.b);
|
||
dist += ABS(d.r - z.r);
|
||
dist += ABS(d.g - z.g);
|
||
dist += ABS(d.b - z.b);
|
||
return dist;
|
||
}
|
||
|
||
static uint16_t *MixBlock(uint16_t *p, struct TtyRgb ttl, struct TtyRgb ttr,
|
||
struct TtyRgb tbl, struct TtyRgb tbr,
|
||
struct TtyRgb qtl, struct TtyRgb qtr,
|
||
struct TtyRgb qbl, struct TtyRgb qbr) {
|
||
unsigned char ttlr, ttlg, ttlb, qtlr, qtlg, qtlb, ttrr, ttrg, ttrb, qtrr,
|
||
qtrg, qtrb, tblr, tblg, tblb, qblr, qblg, qblb, tbrr, tbrg, tbrb, qbrr,
|
||
qbrg, qbrb, l00r, l00g, l00b, l01r, l01g, l01b, l02r, l02g, l02b, l03r,
|
||
l03g, l03b, l04r, l04g, l04b, l05r, l05g, l05b, l06r, l06g, l06b, l07r,
|
||
l07g, l07b, l08r, l08g, l08b, l09r, l09g, l09b, l10r, l10g, l10b, l11r,
|
||
l11g, l11b, l12r, l12g, l12b, l13r, l13g, l13b, l14r, l14g, l14b, l15r,
|
||
l15g, l15b, l16r, l16g, l16b, l17r, l17g, l17b, l18r, l18g, l18b, l19r,
|
||
l19g, l19b, l20r, l20g, l20b, l21r, l21g, l21b, l22r, l22g, l22b, l23r,
|
||
l23g, l23b, l24r, l24g, l24b, l25r, l25g, l25b, l26r, l26g, l26b, l27r,
|
||
l27g, l27b, l28r, l28g, l28b, l29r, l29g, l29b, l30r, l30g, l30b, l31r,
|
||
l31g, l31b;
|
||
unsigned short p00, p01, p02, p03, p04, p05, p06, p07, p08, p09, p10, p11,
|
||
p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26,
|
||
p27, p28, p29, p30, p31;
|
||
ttlr = ttl.r;
|
||
ttlg = ttl.g;
|
||
ttlb = ttl.b;
|
||
qtlr = qtl.r;
|
||
qtlg = qtl.g;
|
||
qtlb = qtl.b;
|
||
ttrr = ttr.r;
|
||
ttrg = ttr.g;
|
||
ttrb = ttr.b;
|
||
qtrr = qtr.r;
|
||
qtrg = qtr.g;
|
||
qtrb = qtr.b;
|
||
tblr = tbl.r;
|
||
tblg = tbl.g;
|
||
tblb = tbl.b;
|
||
qblr = qbl.r;
|
||
qblg = qbl.g;
|
||
qblb = qbl.b;
|
||
tbrr = tbr.r;
|
||
tbrg = tbr.g;
|
||
tbrb = tbr.b;
|
||
qbrr = qbr.r;
|
||
qbrg = qbr.g;
|
||
qbrb = qbr.b;
|
||
l00r = twixt8(qblr, qbrr, 0100);
|
||
l00g = twixt8(qblg, qbrg, 0100);
|
||
l00b = twixt8(qblb, qbrb, 0100);
|
||
l01r = twixt8(qblr, qbrr, 0300);
|
||
l01g = twixt8(qblg, qbrg, 0300);
|
||
l01b = twixt8(qblb, qbrb, 0300);
|
||
l02r = twixt8(qblr, qbrr, 0200);
|
||
l02g = twixt8(qblg, qbrg, 0200);
|
||
l02b = twixt8(qblb, qbrb, 0200);
|
||
l03r = twixt8(qblr, qtlr, 0100);
|
||
l03g = twixt8(qblg, qtlg, 0100);
|
||
l03b = twixt8(qblb, qtlb, 0100);
|
||
l04r = twixt8(qblr, qtlr, 0300);
|
||
l04g = twixt8(qblg, qtlg, 0300);
|
||
l04b = twixt8(qblb, qtlb, 0300);
|
||
l05r = twixt8(qblr, qtlr, 0200);
|
||
l05g = twixt8(qblg, qtlg, 0200);
|
||
l05b = twixt8(qblb, qtlb, 0200);
|
||
l06r = twixt8(qblr, qtrr, 0100);
|
||
l06g = twixt8(qblg, qtrg, 0100);
|
||
l06b = twixt8(qblb, qtrb, 0100);
|
||
l07r = twixt8(qblr, qtrr, 0300);
|
||
l07g = twixt8(qblg, qtrg, 0300);
|
||
l07b = twixt8(qblb, qtrb, 0300);
|
||
l08r = twixt8(qblr, qtrr, 0200);
|
||
l08g = twixt8(qblg, qtrg, 0200);
|
||
l08b = twixt8(qblb, qtrb, 0200);
|
||
l09r = twixt8(qbrr, qtlr, 0100);
|
||
l09g = twixt8(qbrg, qtlg, 0100);
|
||
l09b = twixt8(qbrb, qtlb, 0100);
|
||
l10r = twixt8(qbrr, qtlr, 0300);
|
||
l10g = twixt8(qbrg, qtlg, 0300);
|
||
l10b = twixt8(qbrb, qtlb, 0300);
|
||
l11r = twixt8(qbrr, qtlr, 0200);
|
||
l11g = twixt8(qbrg, qtlg, 0200);
|
||
l11b = twixt8(qbrb, qtlb, 0200);
|
||
l12r = twixt8(qbrr, qtrr, 0100);
|
||
l12g = twixt8(qbrg, qtrg, 0100);
|
||
l12b = twixt8(qbrb, qtrb, 0100);
|
||
l13r = twixt8(qbrr, qtrr, 0300);
|
||
l13g = twixt8(qbrg, qtrg, 0300);
|
||
l13b = twixt8(qbrb, qtrb, 0300);
|
||
l14r = twixt8(qbrr, qtrr, 0200);
|
||
l14g = twixt8(qbrg, qtrg, 0200);
|
||
l14b = twixt8(qbrb, qtrb, 0200);
|
||
l15r = twixt8(qtlr, qblr, 0100);
|
||
l15g = twixt8(qtlg, qblg, 0100);
|
||
l15b = twixt8(qtlb, qblb, 0100);
|
||
l16r = twixt8(qtlr, qblr, 0300);
|
||
l16g = twixt8(qtlg, qblg, 0300);
|
||
l16b = twixt8(qtlb, qblb, 0300);
|
||
l17r = twixt8(qtlr, qblr, 0200);
|
||
l17g = twixt8(qtlg, qblg, 0200);
|
||
l17b = twixt8(qtlb, qblb, 0200);
|
||
l18r = twixt8(qtlr, qbrr, 0100);
|
||
l18g = twixt8(qtlg, qbrg, 0100);
|
||
l18b = twixt8(qtlb, qbrb, 0100);
|
||
l19r = twixt8(qtlr, qbrr, 0300);
|
||
l19g = twixt8(qtlg, qbrg, 0300);
|
||
l19b = twixt8(qtlb, qbrb, 0300);
|
||
l20r = twixt8(qtlr, qbrr, 0200);
|
||
l20g = twixt8(qtlg, qbrg, 0200);
|
||
l20b = twixt8(qtlb, qbrb, 0200);
|
||
l21r = twixt8(qtlr, qtrr, 0100);
|
||
l21g = twixt8(qtlg, qtrg, 0100);
|
||
l21b = twixt8(qtlb, qtrb, 0100);
|
||
l22r = twixt8(qtlr, qtrr, 0300);
|
||
l22g = twixt8(qtlg, qtrg, 0300);
|
||
l22b = twixt8(qtlb, qtrb, 0300);
|
||
l23r = twixt8(qtlr, qtrr, 0200);
|
||
l23g = twixt8(qtlg, qtrg, 0200);
|
||
l23b = twixt8(qtlb, qtrb, 0200);
|
||
l24r = twixt8(qtrr, qblr, 0100);
|
||
l24g = twixt8(qtrg, qblg, 0100);
|
||
l24b = twixt8(qtrb, qblb, 0100);
|
||
l25r = twixt8(qtrr, qblr, 0300);
|
||
l25g = twixt8(qtrg, qblg, 0300);
|
||
l25b = twixt8(qtrb, qblb, 0300);
|
||
l26r = twixt8(qtrr, qblr, 0200);
|
||
l26g = twixt8(qtrg, qblg, 0200);
|
||
l26b = twixt8(qtrb, qblb, 0200);
|
||
l27r = twixt8(qtrr, qbrr, 0100);
|
||
l27g = twixt8(qtrg, qbrg, 0100);
|
||
l27b = twixt8(qtrb, qbrb, 0100);
|
||
l28r = twixt8(qtrr, qbrr, 0300);
|
||
l28g = twixt8(qtrg, qbrg, 0300);
|
||
l28b = twixt8(qtrb, qbrb, 0300);
|
||
l29r = twixt8(qtrr, qbrr, 0200);
|
||
l29g = twixt8(qtrg, qbrg, 0200);
|
||
l29b = twixt8(qtrb, qbrb, 0200);
|
||
l30r = twixt8(qtrr, qtlr, 0100);
|
||
l30g = twixt8(qtrg, qtlg, 0100);
|
||
l30b = twixt8(qtrb, qtlb, 0100);
|
||
l31r = twixt8(qtrr, qtlr, 0300);
|
||
l31g = twixt8(qtrg, qtlg, 0300);
|
||
l31b = twixt8(qtrb, qtlb, 0300);
|
||
p00 = JUDGE(l00);
|
||
p01 = JUDGE(l01);
|
||
p02 = JUDGE(l02);
|
||
p03 = JUDGE(l03);
|
||
p04 = JUDGE(l04);
|
||
p05 = JUDGE(l05);
|
||
p06 = JUDGE(l06);
|
||
p07 = JUDGE(l07);
|
||
p08 = JUDGE(l08);
|
||
p09 = JUDGE(l09);
|
||
p10 = JUDGE(l10);
|
||
p11 = JUDGE(l11);
|
||
p12 = JUDGE(l12);
|
||
p13 = JUDGE(l13);
|
||
p14 = JUDGE(l14);
|
||
p15 = JUDGE(l15);
|
||
p16 = JUDGE(l16);
|
||
p17 = JUDGE(l17);
|
||
p18 = JUDGE(l18);
|
||
p19 = JUDGE(l19);
|
||
p20 = JUDGE(l20);
|
||
p21 = JUDGE(l21);
|
||
p22 = JUDGE(l22);
|
||
p23 = JUDGE(l23);
|
||
p24 = JUDGE(l24);
|
||
p25 = JUDGE(l25);
|
||
p26 = JUDGE(l26);
|
||
p27 = JUDGE(l27);
|
||
p28 = JUDGE(l28);
|
||
p29 = JUDGE(l29);
|
||
p30 = JUDGE(l30);
|
||
p31 = JUDGE(l31);
|
||
*p++ = p00;
|
||
*p++ = p01;
|
||
*p++ = p02;
|
||
*p++ = p03;
|
||
*p++ = p04;
|
||
*p++ = p05;
|
||
*p++ = p06;
|
||
*p++ = p07;
|
||
*p++ = p08;
|
||
*p++ = p09;
|
||
*p++ = p10;
|
||
*p++ = p11;
|
||
*p++ = p12;
|
||
*p++ = p13;
|
||
*p++ = p14;
|
||
*p++ = p15;
|
||
*p++ = p16;
|
||
*p++ = p17;
|
||
*p++ = p18;
|
||
*p++ = p19;
|
||
*p++ = p20;
|
||
*p++ = p21;
|
||
*p++ = p22;
|
||
*p++ = p23;
|
||
*p++ = p24;
|
||
*p++ = p25;
|
||
*p++ = p26;
|
||
*p++ = p27;
|
||
*p++ = p28;
|
||
*p++ = p29;
|
||
*p++ = p30;
|
||
*p++ = p31;
|
||
return p;
|
||
}
|
||
|
||
static struct TtyRgb GetQuant(struct TtyRgb rgb) {
|
||
return g_ansi2rgb_[rgb.xt];
|
||
}
|
||
|
||
static uint16_t *PickUnicode(uint16_t *p, struct TtyRgb tl, struct TtyRgb tr,
|
||
struct TtyRgb bl, struct TtyRgb br,
|
||
struct TtyRgb tl2, struct TtyRgb tr2,
|
||
struct TtyRgb bl2, struct TtyRgb br2) {
|
||
#define PICK(A, B, C, D) *p++ = GetBlockDist(tl, tr, bl, br, A, B, C, D)
|
||
PICK(bl2, bl2, bl2, bl2); /* k=0 bg=bl fg=NULL */
|
||
PICK(bl2, bl2, bl2, br2); /* ▗ k=4 bg=bl fg=br */
|
||
PICK(bl2, bl2, bl2, tl2); /* ▗ k=4 bg=bl fg=tl */
|
||
PICK(bl2, bl2, bl2, tr2); /* ▗ k=4 bg=bl fg=tr */
|
||
PICK(bl2, bl2, br2, bl2); /* ▖ k=5 bg=bl fg=br */
|
||
PICK(bl2, bl2, br2, br2); /* ▄ k=1 bg=bl fg=br */
|
||
PICK(bl2, bl2, tl2, bl2); /* ▖ k=5 bg=bl fg=tl */
|
||
PICK(bl2, bl2, tl2, tl2); /* ▄ k=1 bg=bl fg=tl */
|
||
PICK(bl2, bl2, tr2, bl2); /* ▖ k=5 bg=bl fg=tr */
|
||
PICK(bl2, bl2, tr2, tr2); /* ▄ k=1 bg=bl fg=tr */
|
||
PICK(bl2, br2, bl2, bl2); /* ▝ k=3 bg=bl fg=br */
|
||
PICK(bl2, br2, bl2, br2); /* ▌ k=2 bg=br fg=bl */
|
||
PICK(bl2, br2, br2, bl2); /* ▞ k=6 bg=bl fg=br */
|
||
PICK(bl2, br2, br2, br2); /* ▘ k=7 bg=br fg=bl */
|
||
PICK(bl2, tl2, bl2, bl2); /* ▝ k=3 bg=bl fg=tl */
|
||
PICK(bl2, tl2, bl2, tl2); /* ▌ k=2 bg=tl fg=bl */
|
||
PICK(bl2, tl2, tl2, bl2); /* ▞ k=6 bg=bl fg=tl */
|
||
PICK(bl2, tl2, tl2, tl2); /* ▘ k=7 bg=tl fg=bl */
|
||
PICK(bl2, tr2, bl2, bl2); /* ▝ k=3 bg=bl fg=tr */
|
||
PICK(bl2, tr2, bl2, tr2); /* ▌ k=2 bg=tr fg=bl */
|
||
PICK(bl2, tr2, tr2, bl2); /* ▞ k=6 bg=bl fg=tr */
|
||
PICK(bl2, tr2, tr2, tr2); /* ▘ k=7 bg=tr fg=bl */
|
||
PICK(br2, bl2, bl2, bl2); /* ▘ k=7 bg=bl fg=br */
|
||
PICK(br2, bl2, bl2, br2); /* ▞ k=6 bg=br fg=bl */
|
||
PICK(br2, bl2, br2, bl2); /* ▌ k=2 bg=bl fg=br */
|
||
PICK(br2, bl2, br2, br2); /* ▝ k=3 bg=br fg=bl */
|
||
PICK(br2, br2, bl2, bl2); /* ▄ k=1 bg=br fg=bl */
|
||
PICK(br2, br2, bl2, br2); /* ▖ k=5 bg=br fg=bl */
|
||
PICK(br2, br2, br2, bl2); /* ▗ k=4 bg=br fg=bl */
|
||
PICK(br2, br2, br2, br2); /* k=0 bg=br fg=NULL */
|
||
PICK(br2, br2, br2, tl2); /* ▗ k=4 bg=br fg=tl */
|
||
PICK(br2, br2, br2, tr2); /* ▗ k=4 bg=br fg=tr */
|
||
PICK(br2, br2, tl2, br2); /* ▖ k=5 bg=br fg=tl */
|
||
PICK(br2, br2, tl2, tl2); /* ▄ k=1 bg=br fg=tl */
|
||
PICK(br2, br2, tr2, br2); /* ▖ k=5 bg=br fg=tr */
|
||
PICK(br2, br2, tr2, tr2); /* ▄ k=1 bg=br fg=tr */
|
||
PICK(br2, tl2, br2, br2); /* ▝ k=3 bg=br fg=tl */
|
||
PICK(br2, tl2, br2, tl2); /* ▌ k=2 bg=tl fg=br */
|
||
PICK(br2, tl2, tl2, br2); /* ▞ k=6 bg=br fg=tl */
|
||
PICK(br2, tl2, tl2, tl2); /* ▘ k=7 bg=tl fg=br */
|
||
PICK(br2, tr2, br2, br2); /* ▝ k=3 bg=br fg=tr */
|
||
PICK(br2, tr2, br2, tr2); /* ▌ k=2 bg=tr fg=br */
|
||
PICK(br2, tr2, tr2, br2); /* ▞ k=6 bg=br fg=tr */
|
||
PICK(br2, tr2, tr2, tr2); /* ▘ k=7 bg=tr fg=br */
|
||
PICK(tl2, bl2, bl2, bl2); /* ▘ k=7 bg=bl fg=tl */
|
||
PICK(tl2, bl2, bl2, tl2); /* ▞ k=6 bg=tl fg=bl */
|
||
PICK(tl2, bl2, tl2, bl2); /* ▌ k=2 bg=bl fg=tl */
|
||
PICK(tl2, bl2, tl2, tl2); /* ▝ k=3 bg=tl fg=bl */
|
||
PICK(tl2, br2, br2, br2); /* ▘ k=7 bg=br fg=tl */
|
||
PICK(tl2, br2, br2, tl2); /* ▞ k=6 bg=tl fg=br */
|
||
PICK(tl2, br2, tl2, br2); /* ▌ k=2 bg=br fg=tl */
|
||
PICK(tl2, br2, tl2, tl2); /* ▝ k=3 bg=tl fg=br */
|
||
PICK(tl2, tl2, bl2, bl2); /* ▄ k=1 bg=tl fg=bl */
|
||
PICK(tl2, tl2, bl2, tl2); /* ▖ k=5 bg=tl fg=bl */
|
||
PICK(tl2, tl2, br2, br2); /* ▄ k=1 bg=tl fg=br */
|
||
PICK(tl2, tl2, br2, tl2); /* ▖ k=5 bg=tl fg=br */
|
||
PICK(tl2, tl2, tl2, bl2); /* ▗ k=4 bg=tl fg=bl */
|
||
PICK(tl2, tl2, tl2, br2); /* ▗ k=4 bg=tl fg=br */
|
||
PICK(tl2, tl2, tl2, tl2); /* k=0 bg=tl fg=NULL */
|
||
PICK(tl2, tl2, tl2, tr2); /* ▗ k=4 bg=tl fg=tr */
|
||
PICK(tl2, tl2, tr2, tl2); /* ▖ k=5 bg=tl fg=tr */
|
||
PICK(tl2, tl2, tr2, tr2); /* ▄ k=1 bg=tl fg=tr */
|
||
PICK(tl2, tr2, tl2, tl2); /* ▝ k=3 bg=tl fg=tr */
|
||
PICK(tl2, tr2, tl2, tr2); /* ▌ k=2 bg=tr fg=tl */
|
||
PICK(tl2, tr2, tr2, tl2); /* ▞ k=6 bg=tl fg=tr */
|
||
PICK(tl2, tr2, tr2, tr2); /* ▘ k=7 bg=tr fg=tl */
|
||
PICK(tr2, bl2, bl2, bl2); /* ▘ k=7 bg=bl fg=tr */
|
||
PICK(tr2, bl2, bl2, tr2); /* ▞ k=6 bg=tr fg=bl */
|
||
PICK(tr2, bl2, tr2, bl2); /* ▌ k=2 bg=bl fg=tr */
|
||
PICK(tr2, bl2, tr2, tr2); /* ▝ k=3 bg=tr fg=bl */
|
||
PICK(tr2, br2, br2, br2); /* ▘ k=7 bg=br fg=tr */
|
||
PICK(tr2, br2, br2, tr2); /* ▞ k=6 bg=tr fg=br */
|
||
PICK(tr2, br2, tr2, br2); /* ▌ k=2 bg=br fg=tr */
|
||
PICK(tr2, br2, tr2, tr2); /* ▝ k=3 bg=tr fg=br */
|
||
PICK(tr2, tl2, tl2, tl2); /* ▘ k=7 bg=tl fg=tr */
|
||
PICK(tr2, tl2, tl2, tr2); /* ▞ k=6 bg=tr fg=tl */
|
||
PICK(tr2, tl2, tr2, tl2); /* ▌ k=2 bg=tl fg=tr */
|
||
PICK(tr2, tl2, tr2, tr2); /* ▝ k=3 bg=tr fg=tl */
|
||
PICK(tr2, tr2, bl2, bl2); /* ▄ k=1 bg=tr fg=bl */
|
||
PICK(tr2, tr2, bl2, tr2); /* ▖ k=5 bg=tr fg=bl */
|
||
PICK(tr2, tr2, br2, br2); /* ▄ k=1 bg=tr fg=br */
|
||
PICK(tr2, tr2, br2, tr2); /* ▖ k=5 bg=tr fg=br */
|
||
PICK(tr2, tr2, tl2, tl2); /* ▄ k=1 bg=tr fg=tl */
|
||
PICK(tr2, tr2, tl2, tr2); /* ▖ k=5 bg=tr fg=tl */
|
||
PICK(tr2, tr2, tr2, bl2); /* ▗ k=4 bg=tr fg=bl */
|
||
PICK(tr2, tr2, tr2, br2); /* ▗ k=4 bg=tr fg=br */
|
||
PICK(tr2, tr2, tr2, tl2); /* ▗ k=4 bg=tr fg=tl */
|
||
PICK(tr2, tr2, tr2, tr2); /* k=0 bg=tr fg=NULL */
|
||
#undef PICK
|
||
return p;
|
||
}
|
||
|
||
static uint16_t *PickCp437(uint16_t *p, struct TtyRgb tl, struct TtyRgb tr,
|
||
struct TtyRgb bl, struct TtyRgb br,
|
||
struct TtyRgb tl2, struct TtyRgb tr2,
|
||
struct TtyRgb bl2, struct TtyRgb br2) {
|
||
#define PICK(A, B, C, D) *p++ = GetBlockDist(tl, tr, bl, br, A, B, C, D)
|
||
PICK(bl2, bl2, bl2, bl2); /* k=0 bg=bl fg=NULL */
|
||
PICK(bl2, bl2, br2, br2); /* ▄ k=1 bg=bl fg=br */
|
||
PICK(bl2, bl2, tl2, tl2); /* ▄ k=1 bg=bl fg=tl */
|
||
PICK(bl2, bl2, tr2, tr2); /* ▄ k=1 bg=bl fg=tr */
|
||
PICK(br2, br2, bl2, bl2); /* ▄ k=1 bg=br fg=bl */
|
||
PICK(tl2, tl2, bl2, bl2); /* ▄ k=1 bg=tl fg=bl */
|
||
PICK(tr2, tr2, bl2, bl2); /* ▄ k=1 bg=tr fg=bl */
|
||
PICK(bl2, br2, bl2, br2); /* ▌ k=2 bg=br fg=bl */
|
||
PICK(bl2, tl2, bl2, tl2); /* ▌ k=2 bg=tl fg=bl */
|
||
PICK(bl2, tr2, bl2, tr2); /* ▌ k=2 bg=tr fg=bl */
|
||
PICK(br2, bl2, br2, bl2); /* ▌ k=2 bg=bl fg=br */
|
||
PICK(br2, br2, br2, br2); /* k=0 bg=br fg=NULL */
|
||
PICK(br2, br2, tl2, tl2); /* ▄ k=1 bg=br fg=tl */
|
||
PICK(br2, br2, tr2, tr2); /* ▄ k=1 bg=br fg=tr */
|
||
PICK(br2, tl2, br2, tl2); /* ▌ k=2 bg=tl fg=br */
|
||
PICK(br2, tr2, br2, tr2); /* ▌ k=2 bg=tr fg=br */
|
||
PICK(tl2, bl2, tl2, bl2); /* ▌ k=2 bg=bl fg=tl */
|
||
PICK(tl2, br2, tl2, br2); /* ▌ k=2 bg=br fg=tl */
|
||
PICK(tl2, tl2, br2, br2); /* ▄ k=1 bg=tl fg=br */
|
||
PICK(tl2, tl2, tl2, tl2); /* k=0 bg=tl fg=NULL */
|
||
PICK(tl2, tl2, tr2, tr2); /* ▄ k=1 bg=tl fg=tr */
|
||
PICK(tl2, tr2, tl2, tr2); /* ▌ k=2 bg=tr fg=tl */
|
||
PICK(tr2, bl2, tr2, bl2); /* ▌ k=2 bg=bl fg=tr */
|
||
PICK(tr2, br2, tr2, br2); /* ▌ k=2 bg=br fg=tr */
|
||
PICK(tr2, tl2, tr2, tl2); /* ▌ k=2 bg=tl fg=tr */
|
||
PICK(tr2, tr2, br2, br2); /* ▄ k=1 bg=tr fg=br */
|
||
PICK(tr2, tr2, tl2, tl2); /* ▄ k=1 bg=tr fg=tl */
|
||
PICK(tr2, tr2, tr2, tr2); /* k=0 bg=tr fg=NULL */
|
||
#undef PICK
|
||
return p;
|
||
}
|
||
|
||
static struct Pick PickBlockUnicodeAnsi(struct TtyRgb tl, struct TtyRgb tr,
|
||
struct TtyRgb bl, struct TtyRgb br) {
|
||
struct TtyRgb tl2 = GetQuant(tl);
|
||
struct TtyRgb tr2 = GetQuant(tr);
|
||
struct TtyRgb bl2 = GetQuant(bl);
|
||
struct TtyRgb br2 = GetQuant(br);
|
||
unsigned i, p1, p2;
|
||
uint16_t picks1[96] forcealign(32);
|
||
uint16_t picks2[32] forcealign(32);
|
||
memset(picks1, 0x79, sizeof(picks1));
|
||
memset(picks2, 0x79, sizeof(picks2));
|
||
PickUnicode(picks1, tl, tr, bl, br, tl2, tr2, bl2, br2);
|
||
MixBlock(picks2, tl, tr, bl, br, tl2, tr2, bl2, br2);
|
||
p1 = windex(picks1, 96);
|
||
p2 = windex(picks2, 32);
|
||
return picks1[p1] <= picks2[p2] ? kPicksUnicode[p1] : kPicksMixBlock[p2];
|
||
}
|
||
|
||
static struct Pick PickBlockUnicodeTrue(struct TtyRgb tl, struct TtyRgb tr,
|
||
struct TtyRgb bl, struct TtyRgb br) {
|
||
unsigned i;
|
||
uint16_t picks[96] forcealign(32);
|
||
memset(picks, 0x79, sizeof(picks));
|
||
PickUnicode(picks, tl, tr, bl, br, tl, tr, bl, br);
|
||
i = windex(picks, 96);
|
||
if (i >= 88) {
|
||
unsigned j;
|
||
fprintf(stderr, "uint16_t picks[96] = {");
|
||
for (j = 0; j < 96; ++j) {
|
||
fprintf(stderr, "%3d,", picks[j]);
|
||
}
|
||
fprintf(stderr, "}\n");
|
||
}
|
||
CHECK_LT(i, 88);
|
||
return kPicksUnicode[i];
|
||
}
|
||
|
||
static struct Pick PickBlockCp437Ansi(struct TtyRgb tl, struct TtyRgb tr,
|
||
struct TtyRgb bl, struct TtyRgb br) {
|
||
struct TtyRgb tl2 = GetQuant(tl);
|
||
struct TtyRgb tr2 = GetQuant(tr);
|
||
struct TtyRgb bl2 = GetQuant(bl);
|
||
struct TtyRgb br2 = GetQuant(br);
|
||
unsigned i, p1, p2;
|
||
uint16_t picks1[32] forcealign(32);
|
||
uint16_t picks2[32] forcealign(32);
|
||
memset(picks1, 0x79, sizeof(picks1));
|
||
memset(picks2, 0x79, sizeof(picks2));
|
||
PickCp437(picks1, tl, tr, bl, br, tl2, tr2, bl2, br2);
|
||
MixBlock(picks2, tl, tr, bl, br, tl2, tr2, bl2, br2);
|
||
p1 = windex(picks1, 32);
|
||
p2 = windex(picks2, 32);
|
||
return picks1[p1] <= picks2[p2] ? kPicksCp437[p1] : kPicksMixBlock[p2];
|
||
}
|
||
|
||
static struct Pick PickBlockCp437True(struct TtyRgb tl, struct TtyRgb tr,
|
||
struct TtyRgb bl, struct TtyRgb br) {
|
||
unsigned i;
|
||
uint16_t picks[32] forcealign(32);
|
||
memset(picks, 0x79, sizeof(picks));
|
||
PickCp437(picks, tl, tr, bl, br, tl, tr, bl, br);
|
||
return kPicksCp437[windex(picks, 32)];
|
||
}
|
||
|
||
static char *CopyGlyph(char *v, struct Glyph glyph) {
|
||
memcpy(v, &glyph, 4);
|
||
return v + glyph.len;
|
||
}
|
||
|
||
static char *CopyBlock(char *v, const struct TtyRgb chunk[hasatleast 4],
|
||
struct Pick pick, struct TtyRgb *bg, struct TtyRgb *fg,
|
||
struct Glyph *glyph) {
|
||
unsigned i;
|
||
CHECK_LT(pick.bg, 4);
|
||
if (pick.fg != 0xff) CHECK_LT(pick.fg, 4);
|
||
i = 0;
|
||
if (pick.fg == 0xff) {
|
||
if (!ttyeq(*bg, chunk[pick.bg])) {
|
||
if (ttyeq(*fg, chunk[pick.bg])) {
|
||
if (memcmp(glyph, &kGlyphs[1][0], sizeof(*glyph)) == 0) {
|
||
v = setbg(v, (*bg = *fg));
|
||
} else {
|
||
i = 1;
|
||
}
|
||
} else {
|
||
v = setbg(v, (*bg = chunk[pick.bg]));
|
||
}
|
||
}
|
||
} else if (ttyeq(chunk[pick.bg], chunk[pick.fg])) {
|
||
pick.k = 0;
|
||
if (!ttyeq(*bg, chunk[pick.bg])) {
|
||
if (ttyeq(*fg, chunk[pick.bg])) {
|
||
if (memcmp(glyph, &kGlyphs[1][0], sizeof(*glyph)) == 0) {
|
||
v = setbg(v, (*bg = *fg));
|
||
} else {
|
||
i = 1;
|
||
}
|
||
} else {
|
||
v = setbg(v, (*bg = chunk[pick.bg]));
|
||
}
|
||
}
|
||
} else if (!ttyeq(*fg, chunk[pick.fg])) {
|
||
if (!ttyeq(*bg, chunk[pick.bg])) {
|
||
if (ttyeq(*fg, chunk[pick.bg]) && ttyeq(*bg, chunk[pick.fg])) {
|
||
if (pick.k == 0 && memcmp(glyph, &kGlyphs[1][0], sizeof(*glyph)) == 0) {
|
||
v = setbg(v, (*bg = *fg));
|
||
} else {
|
||
i = 1;
|
||
}
|
||
} else {
|
||
v = setbgfg(v, (*bg = chunk[pick.bg]), (*fg = chunk[pick.fg]));
|
||
}
|
||
} else {
|
||
v = setfg(v, (*fg = chunk[pick.fg]));
|
||
}
|
||
} else if (!ttyeq(*bg, chunk[pick.bg])) {
|
||
v = setbg(v, (*bg = chunk[pick.bg]));
|
||
}
|
||
return CopyGlyph(v, (*glyph = kGlyphs[i][pick.k]));
|
||
}
|
||
|
||
static bool ChunkEq(struct TtyRgb c[hasatleast 4],
|
||
struct TtyRgb c2[hasatleast 4]) {
|
||
return ttyeq(c[TL], c[TR]) && ttyeq(c[BL], c[BR]) && ttyeq(c2[TL], c2[TR]) &&
|
||
ttyeq(c2[BL], c2[BR]);
|
||
}
|
||
|
||
static struct TtyRgb *CopyChunk(struct TtyRgb chunk[hasatleast 4],
|
||
const struct TtyRgb *c, size_t n) {
|
||
chunk[TL] = c[0 + 0];
|
||
chunk[TR] = c[0 + 1];
|
||
chunk[BL] = c[n + 0];
|
||
chunk[BR] = c[n + 1];
|
||
return chunk;
|
||
}
|
||
|
||
static noinline char *CopyRun(char *v, size_t n,
|
||
struct TtyRgb lastchunk[hasatleast 4],
|
||
const struct TtyRgb **c, size_t *x,
|
||
struct TtyRgb *bg, struct TtyRgb *fg,
|
||
struct Glyph *glyph) {
|
||
struct TtyRgb chunk[4];
|
||
if (memcmp(glyph, &kGlyphs[1][0], sizeof(*glyph)) == 0) {
|
||
if (!ttyeq(*bg, *fg)) {
|
||
v = setbg(v, (*bg = *fg));
|
||
}
|
||
*glyph = kGlyphs[0][0];
|
||
}
|
||
do {
|
||
v = CopyGlyph(v, *glyph);
|
||
*x += 2;
|
||
*c += 2;
|
||
if (*x >= n) break;
|
||
CopyChunk(chunk, *c, n);
|
||
} while (ChunkEq(chunk, lastchunk));
|
||
*x -= 2;
|
||
*c -= 2;
|
||
return v;
|
||
}
|
||
|
||
/**
|
||
* Maps 2×2 pixel chunks onto ANSI UNICODE cells.
|
||
* @note h/t Nick Black for his quadrant blitting work on notcurses
|
||
*/
|
||
char *ttyraster(char *v, const struct TtyRgb *c, size_t yn, size_t n,
|
||
struct TtyRgb bg, struct TtyRgb fg) {
|
||
unsigned y, x;
|
||
struct Pick p;
|
||
struct Glyph glyph;
|
||
struct TtyRgb chun[4], lastchunk[4];
|
||
for (y = 0; y < yn; y += 2, c += n) {
|
||
if (y) *v++ = '\r', *v++ = '\n';
|
||
for (x = 0; x < n; x += 2, c += 2) {
|
||
CopyChunk(chun, c, n);
|
||
if (ttyquant()->alg == kTtyQuantTrue) {
|
||
if (ttyquant()->blocks == kTtyBlocksCp437) {
|
||
p = PickBlockCp437True(chun[TL], chun[TR], chun[BL], chun[BR]);
|
||
} else {
|
||
p = PickBlockUnicodeTrue(chun[TL], chun[TR], chun[BL], chun[BR]);
|
||
}
|
||
} else {
|
||
if (ttyquant()->blocks == kTtyBlocksCp437) {
|
||
p = PickBlockCp437Ansi(chun[TL], chun[TR], chun[BL], chun[BR]);
|
||
} else {
|
||
p = PickBlockUnicodeAnsi(chun[TL], chun[TR], chun[BL], chun[BR]);
|
||
}
|
||
}
|
||
v = CopyBlock(v, chun, p, &bg, &fg, &glyph);
|
||
memcpy(lastchunk, chun, sizeof(chun));
|
||
}
|
||
}
|
||
*v = '\0';
|
||
return v;
|
||
}
|