commit hxtools for openSUSE:Factory
Hello community, here is the log from the commit of package hxtools for openSUSE:Factory checked in at 2020-04-29 20:51:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hxtools (Old) and /work/SRC/openSUSE:Factory/.hxtools.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "hxtools" Wed Apr 29 20:51:25 2020 rev:39 rq:798739 version:20200428 Changes: -------- --- /work/SRC/openSUSE:Factory/hxtools/hxtools.changes 2020-03-31 17:16:00.531656694 +0200 +++ /work/SRC/openSUSE:Factory/.hxtools.new.2738/hxtools.changes 2020-04-29 20:53:27.677146159 +0200 @@ -1,0 +2,10 @@ +Tue Apr 28 21:27:57 UTC 2020 - Jan Engelhardt <jengelh@inai.de> + +- Update to release 20200428 + * vfontas: N2 vectorizer: cease smoothing of stub serifs + (concerns the topleft corner of an 'E' in some fonts) + * vfontas: N2 vectorizer: avoid diagonalizing "pimples" in font + outlines (concerns 1-pixel protrusions in the right vertical + edge of the '4') + +------------------------------------------------------------------- Old: ---- hxtools-20200331.tar.asc hxtools-20200331.tar.xz New: ---- hxtools-20200428.tar.asc hxtools-20200428.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hxtools.spec ++++++ --- /var/tmp/diff_new_pack.95dFXb/_old 2020-04-29 20:53:28.581148229 +0200 +++ /var/tmp/diff_new_pack.95dFXb/_new 2020-04-29 20:53:28.585148239 +0200 @@ -17,17 +17,16 @@ Name: hxtools -Version: 20200331 +Version: 20200428 Release: 0 Summary: Collection of day-to-day tools (binaries) License: GPL-2.0+ and WTFPL Group: Productivity/Other -Url: http://inai.de/projects/hxtools/ +Url: https://inai.de/projects/hxtools/ -Source: http://jftp.inai.de/hxtools/%name-%version.tar.xz -Source2: http://jftp.inai.de/hxtools/%name-%version.tar.asc +Source: https://inai.de/files/hxtools/%name-%version.tar.xz +Source2: https://inai.de/files/hxtools/%name-%version.tar.asc Source3: %name.keyring -BuildRequires: bdftopcf BuildRequires: fdupes BuildRequires: gcc-c++ BuildRequires: libcap-devel >= 2 ++++++ hxtools-20200331.tar.xz -> hxtools-20200428.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hxtools-20200331/configure new/hxtools-20200428/configure --- old/hxtools-20200331/configure 2020-03-31 01:26:20.475923272 +0200 +++ new/hxtools-20200428/configure 2020-04-28 23:16:35.897024624 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for hxtools 20200331. +# Generated by GNU Autoconf 2.69 for hxtools 20200428. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ # Identity of this package. PACKAGE_NAME='hxtools' PACKAGE_TARNAME='hxtools' -PACKAGE_VERSION='20200331' -PACKAGE_STRING='hxtools 20200331' +PACKAGE_VERSION='20200428' +PACKAGE_STRING='hxtools 20200428' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1301,7 +1301,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures hxtools 20200331 to adapt to many kinds of systems. +\`configure' configures hxtools 20200428 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1367,7 +1367,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of hxtools 20200331:";; + short | recursive ) echo "Configuration of hxtools 20200428:";; esac cat <<\_ACEOF @@ -1486,7 +1486,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -hxtools configure 20200331 +hxtools configure 20200428 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1946,7 +1946,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by hxtools $as_me 20200331, which was +It was created by hxtools $as_me 20200428, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2811,7 +2811,7 @@ # Define the identity of the package. PACKAGE='hxtools' - VERSION='20200331' + VERSION='20200428' cat >>confdefs.h <<_ACEOF @@ -2989,6 +2989,45 @@ fi fi +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=0;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' @@ -5897,7 +5936,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by hxtools $as_me 20200331, which was +This file was extended by hxtools $as_me 20200428, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -5963,7 +6002,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -hxtools config.status 20200331 +hxtools config.status 20200428 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hxtools-20200331/configure.ac new/hxtools-20200428/configure.ac --- old/hxtools-20200331/configure.ac 2020-03-31 01:25:27.000000000 +0200 +++ new/hxtools-20200428/configure.ac 2020-04-28 22:57:45.000000000 +0200 @@ -1,8 +1,9 @@ -AC_INIT([hxtools], [20200331]) +AC_INIT([hxtools], [20200428]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_HEADERS([config.h]) AC_PROG_INSTALL AM_INIT_AUTOMAKE([-Wall foreign subdir-objects tar-pax no-dist-gzip dist-xz]) +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CXX diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hxtools-20200331/doc/NEWS.txt new/hxtools-20200428/doc/NEWS.txt --- old/hxtools-20200331/doc/NEWS.txt 2020-03-31 01:25:27.000000000 +0200 +++ new/hxtools-20200428/doc/NEWS.txt 2020-04-28 22:57:45.000000000 +0200 @@ -1,4 +1,12 @@ +rel-20200428 +============ +* vfontas: N2 vectorizer: cease smoothing of stub serifs + (concerns the topleft corner of an 'E' in some fonts) +* vfontas: N2 vectorizer: avoid diagonalizing "pimples" in font outlines + (concerns 1-pixel protrusions in the right vertical edge of the '4') + + rel-20200331 ============ * vfontas: add new N2 vectorizer @@ -15,7 +23,6 @@ addressed in a later release. N2: -* Serifs are subject to diagonalization (but maybe should become exempt). * Isthmuses tend to get washed out, noticable in glyphs like 'G'. * Sumps in the 'K', 'M', 'N' glyphs could be improved. This is noticable in glyphs like 'G'. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hxtools-20200331/smm/vfalib.cpp new/hxtools-20200428/smm/vfalib.cpp --- old/hxtools-20200331/smm/vfalib.cpp 2020-03-31 01:25:27.000000000 +0200 +++ new/hxtools-20200428/smm/vfalib.cpp 2020-04-28 22:57:45.000000000 +0200 @@ -39,11 +39,17 @@ namespace vfalib { enum { + PSF1_MAGIC0 = 0x36, + PSF1_MAGIC1 = 0x04, + PSF1_MF_512 = 1 << 0, + PSF1_MF_TAB = 1 << 1, + PSF1_MF_SEQ = 1 << 2, PSF2_MAGIC0 = 0x72, PSF2_MAGIC1 = 0xB5, PSF2_MAGIC2 = 0x4A, PSF2_MAGIC3 = 0x86, PSF2_HAS_UNICODE_TABLE = 0x01, + VFA_UCS2 = 0x8000, PSF2_SEPARATOR = 0xFF, PSF2_STARTSEQ = 0xFE, }; @@ -66,16 +72,24 @@ class vectorizer final { public: - std::vector<std::vector<edge>> simple(const glyph &, int descent = 0); - std::vector<std::vector<edge>> n1(const glyph &, int descent = 0); - std::vector<std::vector<edge>> n2(const glyph &, int descent = 0); - static constexpr const unsigned int scale_factor = 2; + vectorizer(const glyph &, int descent = 0); + std::vector<std::vector<edge>> simple(); + std::vector<std::vector<edge>> n1(); + std::vector<std::vector<edge>> n2(unsigned int flags = 0); + + static constexpr const int scale_factor = 2; + static const unsigned int P_ISTHMUS = 1 << 1; private: - void finalize(); + void make_squares(); + void internal_edge_delete(); + unsigned int neigh_edges(unsigned int dir, const vertex &, std::set<edge>::iterator &, std::set<edge>::iterator &) const; + std::set<edge>::iterator next_edge(unsigned int dir, const edge &, unsigned int flags) const; std::vector<edge> pop_poly(unsigned int flags); void set(int, int); + const glyph &m_glyph; + int m_descent = 0; std::set<edge> emap; static const unsigned int P_SIMPLIFY_LINES = 1 << 0; }; @@ -355,25 +369,69 @@ return uc; } +static char32_t nextucs2(FILE *fp) +{ + auto x = fgetc(fp); + if (x == EOF) + return ~0U; + auto y = fgetc(fp); + if (y == EOF) + return ~0U; + x |= y << 8; + return x < 0xffff ? x : ~0U; +} + +static unsigned int psf_version(FILE *fp) +{ + uint8_t x = fgetc(fp), y = fgetc(fp); + if (x == PSF1_MAGIC0 && y == PSF1_MAGIC1) + return 1; + if (x != PSF2_MAGIC0 || y != PSF2_MAGIC1) + return 0; + x = fgetc(fp); + y = fgetc(fp); + return x == PSF2_MAGIC2 && y == PSF2_MAGIC3 ? 2 : 0; +} + int font::load_psf(const char *file) { std::unique_ptr<FILE, deleter> fp(fopen(file, "rb")); if (fp == nullptr) return -errno; - struct psf2_header hdr; - if (fread(&hdr, sizeof(hdr), 1, fp.get()) != 1) - return -errno; - hdr.version = le32_to_cpu(hdr.version); - hdr.headersize = le32_to_cpu(hdr.headersize); - hdr.flags = le32_to_cpu(hdr.flags); - hdr.length = le32_to_cpu(hdr.length); - hdr.charsize = le32_to_cpu(hdr.charsize); - hdr.height = le32_to_cpu(hdr.height); - hdr.width = le32_to_cpu(hdr.width); - if (hdr.magic[0] != PSF2_MAGIC0 || hdr.magic[1] != PSF2_MAGIC1 || - hdr.magic[2] != PSF2_MAGIC2 || hdr.magic[3] != PSF2_MAGIC3 || - hdr.version != 0) + + struct psf2_header hdr{}; + switch (psf_version(fp.get())) { + case 0: return -EINVAL; + case 1: { + auto mode = fgetc(fp.get()), charsize = fgetc(fp.get()); + if (mode == EOF || charsize == EOF) + return -EINVAL; + hdr.length = (mode & PSF1_MF_512) ? 512 : 256; + hdr.charsize = charsize; + hdr.height = charsize; + hdr.width = 8; + hdr.flags |= VFA_UCS2; + if (mode & (PSF1_MF_TAB | PSF1_MF_SEQ)) + hdr.flags |= PSF2_HAS_UNICODE_TABLE; + break; + } + case 2: { + if (fread(&hdr.version, sizeof(hdr) - offsetof(decltype(hdr), version), 1, fp.get()) != 1 || + le32_to_cpu(hdr.version) != 0) + return -EINVAL; + hdr.version = le32_to_cpu(hdr.version); + if (hdr.version != 0) + return -EINVAL; + hdr.headersize = le32_to_cpu(hdr.headersize); + hdr.flags = le32_to_cpu(hdr.flags); + hdr.length = le32_to_cpu(hdr.length); + hdr.charsize = le32_to_cpu(hdr.charsize); + hdr.height = le32_to_cpu(hdr.height); + hdr.width = le32_to_cpu(hdr.width); + break; + } + } std::unique_ptr<char[]> buf(new char[hdr.charsize]); size_t glyph_start = m_glyph.size(); @@ -386,15 +444,9 @@ if (!(hdr.flags & PSF2_HAS_UNICODE_TABLE)) return 0; m_unicode_map = std::make_shared<unicode_map>(); - auto cd = iconv_open("UTF-32", "UTF-8"); - if (cd == nullptr) { - fprintf(stderr, "iconv_open: %s\n", strerror(errno)); - return -errno; - } - auto cdclean = make_scope_success([&]() { iconv_close(cd); }); for (unsigned int idx = 0; idx < hdr.length; ++idx) { do { - auto uc = nextutf8(fp.get()); + auto uc = hdr.flags & VFA_UCS2 ? nextucs2(fp.get()) : nextutf8(fp.get()); if (uc == ~0U) break; m_unicode_map->add_i2u(glyph_start + idx, uc); @@ -721,6 +773,24 @@ return 0; } +static inline bool testbit_c(const glyph &g, int x, int y) +{ + if (x < 0 || y < 0 || x >= static_cast<int>(g.m_size.w) || y >= static_cast<int>(g.m_size.h)) + return false; + bitpos bp = y * g.m_size.w + x; + return g.m_data[bp.byte] & bp.mask; +} + +static inline bool testbit_u(const glyph &g, int x, int y) +{ + bitpos bp = y * g.m_size.w + x; + return g.m_data[bp.byte] & bp.mask; +} + +vectorizer::vectorizer(const glyph &g, int desc) : + m_glyph(g), m_descent(desc) +{} + void vectorizer::set(int x, int y) { /* TTF/OTF spec wants CCW orientation */ @@ -733,7 +803,20 @@ emap.insert(edge{{x + s, y}, {x, y}}); } -void vectorizer::finalize() +void vectorizer::make_squares() +{ + const auto &sz = m_glyph.m_size; + for (unsigned int y = 0; y < sz.h; ++y) { + int yy = sz.h - 1 - static_cast<int>(y) - m_descent; + for (unsigned int x = 0; x < sz.w; ++x) { + bitpos ipos = y * sz.w + x; + if (m_glyph.m_data[ipos.byte] & ipos.mask) + set(x, yy); + } + } +} + +void vectorizer::internal_edge_delete() { /* * Remove overlaps: As enforced by set(), all the polygons are added @@ -759,6 +842,107 @@ } } +/** + * Find the next edges (up to two) for @tail. + */ +unsigned int vectorizer::neigh_edges(unsigned int cur_dir, const vertex &tail, + std::set<edge>::iterator &inward, std::set<edge>::iterator &outward) const +{ + inward = emap.lower_bound({tail, {INT_MIN, INT_MIN}}); + if (inward == emap.end() || inward->start_vtx != tail) { + outward = inward = emap.end(); + return 0; + } + outward = std::next(inward); /* due to sortedness of @emap */ + if (outward == emap.cend() || outward->start_vtx != tail) { + outward = emap.end(); + return 1; + } + if (cur_dir == 0 || cur_dir == 270) + std::swap(inward, outward); /* order of @emap */ + return 2; +} + +std::set<edge>::iterator vectorizer::next_edge(unsigned int cur_dir, + const edge &cur_edge, unsigned int flags) const +{ + const auto &tail = cur_edge.end_vtx; + std::set<edge>::iterator inward, outward; + auto ret = neigh_edges(cur_dir, tail, inward, outward); + if (!(flags & P_ISTHMUS) || ret <= 1) + return inward; + /* + * If there are two edges with the same vertex, we have + * an intersection ahead (illustrative): + * + * ##..##.. ..##..## ####.... ....#### + * ..##.... ....##.. ####..## ##..#### + * ##..#### ####..## ....##.. ..##.... + * ....#### ####.... ..##..## ##..##.. + * + * n2_angle will work with the polygon edge we determine here, so the + * choice of walking direction matters. + * + * We are working with lines rather than pixels, but every edge's right + * side corresponds to a pixel, thereby the bitmap could be + * reconstructed. But since we have a reference to the bitmap anyway, + * it can just be checked directly. + * + * Antijoinworthy patterns: + * (A1) (A2) + * ....##.. ..##.... + * ..##.... ####.... + * ##..#### ....#### + * ....#### ....#### + * Joinworthy: + * (J1) + * ..MM.... + * ..MM.... + * ....#### + * ....#### + * + * Right now, we are only testing for A1+A2. Might be enough...? + */ + vertex bmp; + if (cur_dir == 0) + bmp = cur_edge.start_vtx; + else if (cur_dir == 90) + bmp = {cur_edge.start_vtx.x, cur_edge.start_vtx.y - scale_factor}; + else if (cur_dir == 180) + bmp = {cur_edge.start_vtx.x - scale_factor, cur_edge.end_vtx.y}; + else if (cur_dir == 270) + bmp = cur_edge.end_vtx; + bmp.x /= scale_factor; + bmp.y /= scale_factor; + bmp.y = m_glyph.m_size.h - bmp.y - m_descent - 1; + + /* Test for pattern A1 */ + bool up = testbit_c(m_glyph, bmp.x, bmp.y - 2); + bool right = testbit_c(m_glyph, bmp.x + 2, bmp.y); + bool down = testbit_c(m_glyph, bmp.x, bmp.y + 2); + bool left = testbit_c(m_glyph, bmp.x - 2, bmp.y); + if (cur_dir == 0 && left && up) + return inward; + if (cur_dir == 90 && up && right) + return inward; + if (cur_dir == 180 && right && down) + return inward; + if (cur_dir == 270 && down && left) + return inward; + + /* Test for pattern A2 */ + if (cur_dir == 0 && testbit_c(m_glyph, bmp.x - 2, bmp.y - 1) && testbit_c(m_glyph, bmp.x - 1, bmp.y - 2)) + return inward; + if (cur_dir == 90 && testbit_c(m_glyph, bmp.x + 1, bmp.y - 2) && testbit_c(m_glyph, bmp.x + 2, bmp.y - 1)) + return inward; + if (cur_dir == 180 && testbit_c(m_glyph, bmp.x + 2, bmp.y + 1) && testbit_c(m_glyph, bmp.x + 1, bmp.y + 2)) + return inward; + if (cur_dir == 270 && testbit_c(m_glyph, bmp.x - 2, bmp.y + 1) && testbit_c(m_glyph, bmp.x - 1, bmp.y + 2)) + return inward; + + return outward; +} + std::vector<edge> vectorizer::pop_poly(unsigned int flags) { std::vector<edge> poly; @@ -774,43 +958,13 @@ auto &tail_vtx = poly.rbegin()->end_vtx; if (tail_vtx == poly.cbegin()->start_vtx) break; - auto next = emap.lower_bound({tail_vtx, {INT_MIN, INT_MIN}}); + auto next = next_edge(prev_dir, *poly.rbegin(), flags); if (next == emap.cend()) { fprintf(stderr, "unclosed poly wtf?!\n"); break; } /* - * If there are two edges from a given start vertex, prefer the - * edge which makes an inward curving. This makes a shape like - * - * ###### - * ## ## - * #### - * - * be emitted a single polygon, rather than two (outer & - * enclave). The tradeoff is that fully-enclosed enclaves, e.g. - * - * ######## - * ## #### - * #### ## - * ######## - * - * will favor making a single polygon with self-intersection. - * The enclave of the number '4', when a 1-px stroke thickness - * is used, also ceases to be an enclave. - * - * (None of all this has an effect on rendering, just the way - * font editors see the outline.) - */ - if (prev_dir == 0 || prev_dir == 270) { - /* (Exploiting the sortedness of emap here.) */ - auto cand2 = std::next(next); - if (cand2 != emap.cend() && cand2->start_vtx == tail_vtx) - next = cand2; - } - - /* * Skip redundant vertices along the way to the next * directional change of the outline. (Vertices are not * deleted, and they are also duplicated, in case another @@ -827,18 +981,10 @@ return poly; } -std::vector<std::vector<edge>> vectorizer::simple(const glyph &g, int desc) +std::vector<std::vector<edge>> vectorizer::simple() { - const auto &sz = g.m_size; - for (unsigned int y = 0; y < sz.h; ++y) { - int yy = sz.h - 1 - static_cast<int>(y) - desc; - for (unsigned int x = 0; x < sz.w; ++x) { - bitpos ipos = y * sz.w + x; - if (g.m_data[ipos.byte] & ipos.mask) - set(x, yy); - } - } - finalize(); + make_squares(); + internal_edge_delete(); std::vector<std::vector<edge>> pmap; while (true) { auto poly = pop_poly(P_SIMPLIFY_LINES); @@ -849,25 +995,12 @@ return pmap; } -static inline bool testbit_c(const glyph &g, int x, int y) -{ - if (x < 0 || y < 0 || x >= static_cast<int>(g.m_size.w) || y >= static_cast<int>(g.m_size.h)) - return false; - bitpos bp = y * g.m_size.w + x; - return g.m_data[bp.byte] & bp.mask; -} - -static inline bool testbit_u(const glyph &g, int x, int y) -{ - bitpos bp = y * g.m_size.w + x; - return g.m_data[bp.byte] & bp.mask; -} - -std::vector<std::vector<edge>> vectorizer::n1(const glyph &g, int desc) +std::vector<std::vector<edge>> vectorizer::n1() { + auto &g = m_glyph; const auto &sz = g.m_size; for (unsigned int uy = 0; uy < sz.h; ++uy) { - int y = sz.h - 1 - static_cast<int>(uy) - desc; + int y = sz.h - 1 - static_cast<int>(uy) - m_descent; for (unsigned int ux = 0; ux < sz.w; ++ux) { bitpos ipos = uy * sz.w + ux; int x = ux; @@ -917,7 +1050,7 @@ } } - finalize(); + internal_edge_delete(); std::vector<std::vector<edge>> pmap; while (true) { auto poly = pop_poly(P_SIMPLIFY_LINES); @@ -930,7 +1063,8 @@ static void n2_angle(std::vector<edge> &poly) { - static const unsigned int M_HEAD = 0x20, M_TAIL = 0x02; + static const unsigned int M_HEAD = 0x20, M_TAIL = 0x02, + M_XHEAD = 0x10, M_XTAIL = 0x01; std::vector<unsigned int> flags(poly.size()); for (size_t xm3 = 0; xm3 < poly.size(); ++xm3) { @@ -956,9 +1090,15 @@ /* _|~|_ or ~|_|~ pattern seen */ if ((dm3 == d00 || dm3 == dp1) && (dp3 == d00 || dp3 == dm1) && - dm1 == (dm2 + 270) % 360 && dp1 == (dm2 + 90) % 360) + dm1 == (dm2 + 270) % 360 && dp1 == (dm2 + 90) % 360) { /* pimple __|~|__ ('f', '4'), retain */ + flags[xm2] |= M_XTAIL; + flags[xm1] = M_XHEAD | M_XTAIL; + flags[x00] = M_XHEAD | M_XTAIL; + flags[xp1] = M_XHEAD | M_XTAIL; + flags[xp2] |= M_XHEAD; continue; + } if (dm1 == (dm2 + 90) % 360 && dp1 == (dm2 + 270) % 360) { /* dimple ~~|_|~~ ('8'), sink it */ @@ -984,6 +1124,15 @@ if ((d00 + 270) % 360 != dp1 && (d00 + 90) % 360 != dp1) continue; + /* #5: serif (ramp), topleft of 'E' */ + if (dm2 == dm1 && d00 == (dm1 + 270) % 360 && + dp1 == dm1 && dp2 == (dm1 + 90) % 360 && dp3 == dp2) + continue; + /* bottomleft 'E' */ + if (dm3 == dm2 && dm1 == (dm2 + 90) % 360 && + d00 == (dm2 + 180) % 360 && dp1 == dm1 && dp2 == dp1) + continue; + /* * #1: single step, with or without sump, * #2: bottom of stairs, with or without sump, @@ -1011,6 +1160,8 @@ auto ib = ix % poly.size(); if (!(flags[ia] & M_TAIL && flags[ib] & M_HEAD)) continue; + if ((flags[ia] & M_XTAIL) || (flags[ib] & M_XHEAD)) + continue; flags[ia] &= ~M_TAIL; flags[ib] &= ~M_HEAD; @@ -1058,22 +1209,15 @@ } } -std::vector<std::vector<edge>> vectorizer::n2(const glyph &g, int desc) +std::vector<std::vector<edge>> vectorizer::n2(unsigned int flags) { - const auto &sz = g.m_size; - for (unsigned int y = 0; y < sz.h; ++y) { - int yy = sz.h - 1 - static_cast<int>(y) - desc; - for (unsigned int x = 0; x < sz.w; ++x) { - bitpos ipos = y * sz.w + x; - if (g.m_data[ipos.byte] & ipos.mask) - set(x, yy); - } - } - finalize(); + flags &= P_ISTHMUS; + make_squares(); + internal_edge_delete(); std::vector<std::vector<edge>> pmap; while (true) { /* Have all edges retian length 1 */ - auto poly = pop_poly(0); + auto poly = pop_poly(flags); if (poly.size() == 0) break; n2_angle(poly); @@ -1097,11 +1241,13 @@ std::vector<std::vector<edge>> pmap; if (vt == V_SIMPLE) - pmap = vectorizer().simple(m_glyph[idx], desc); + pmap = vectorizer(m_glyph[idx], desc).simple(); else if (vt == V_N1) - pmap = vectorizer().n1(m_glyph[idx], desc); + pmap = vectorizer(m_glyph[idx], desc).n1(); else if (vt == V_N2) - pmap = vectorizer().n2(m_glyph[idx], desc); + pmap = vectorizer(m_glyph[idx], desc).n2(); + else if (vt == V_N2EV) + pmap = vectorizer(m_glyph[idx], desc).n2(vectorizer::P_ISTHMUS); for (const auto &poly : pmap) { const auto &v1 = poly.cbegin()->start_vtx; fprintf(fp, "%d %d m 25\n", v1.x, v1.y); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hxtools-20200331/smm/vfalib.hpp new/hxtools-20200428/smm/vfalib.hpp --- old/hxtools-20200331/smm/vfalib.hpp 2020-03-31 01:25:27.000000000 +0200 +++ new/hxtools-20200428/smm/vfalib.hpp 2020-04-28 22:57:45.000000000 +0200 @@ -67,12 +67,14 @@ struct vertex { bool operator<(const struct vertex &) const; bool operator==(const struct vertex &) const; + bool operator!=(const struct vertex &a) const { return !operator==(a); } int x, y; }; struct edge { bool operator<(const struct edge &) const; bool operator==(const struct edge &) const; + bool operator!=(const struct edge &a) const { return !operator==(a); } unsigned int trivial_dir() const; struct vertex start_vtx, end_vtx; }; @@ -81,6 +83,7 @@ V_SIMPLE = 0, V_N1, V_N2, + V_N2EV, }; class glyph { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hxtools-20200331/smm/vfontas.cpp new/hxtools-20200428/smm/vfontas.cpp --- old/hxtools-20200331/smm/vfontas.cpp 2020-03-31 01:25:27.000000000 +0200 +++ new/hxtools-20200428/smm/vfontas.cpp 2020-04-28 22:57:45.000000000 +0200 @@ -267,6 +267,15 @@ return false; } +static bool vf_saven2ev(font &f, char **args) +{ + auto ret = f.save_sfd(args[0], vectoalg::V_N2EV); + if (ret >= 0) + return true; + fprintf(stderr, "Error saving %s: %s\n", args[0], strerror(-ret)); + return false; +} + static bool vf_setname(font &f, char **args) { f.name = args[0]; @@ -420,12 +429,13 @@ {"loadhex", 1, vf_loadhex}, {"loadmap", 1, vf_loadmap}, {"loadpsf", 1, vf_loadpsf}, - {"savebdf", 2, vf_savebdf}, + {"savebdf", 1, vf_savebdf}, {"saveclt", 1, vf_saveclt}, {"savefnt", 1, vf_savefnt}, {"savemap", 1, vf_savemap}, {"saven1", 1, vf_saven1}, {"saven2", 1, vf_saven2}, + {"saven2ev", 1, vf_saven2ev}, {"savepbm", 1, vf_savepbm}, {"savepsf", 1, vf_savepsf}, {"savesfd", 1, vf_savesfd}, ++++++ hxtools.keyring ++++++ --- /var/tmp/diff_new_pack.95dFXb/_old 2020-04-29 20:53:28.753148624 +0200 +++ /var/tmp/diff_new_pack.95dFXb/_new 2020-04-29 20:53:28.753148624 +0200 @@ -1,91 +1,56 @@ -pub 4096R/0xA28BDFEEB5A2B2FF 2015-05-07 [expires: 2020-08-08] - Key fingerprint = 2F8B FE96 D14F 8B56 0FBC 1F1D A28B DFEE B5A2 B2FF -uid [ unknown] Jan Engelhardt <jengelh@inai.de> -sub 4096R/0xB927458717B4ECED 2015-05-07 [expires: 2020-08-08] -sub 4096R/0x6A3933DA297DFB7D 2015-05-07 [expires: 2020-08-08] +pub rsa4096/0xD6388181F35A0938 2020-04-28 [SC] [expires: 2025-06-01] + Key fingerprint = 2368 6C10 A456 91BE 7A42 5109 D638 8181 F35A 0938 +uid [ultimate] Jan Engelhardt <jengelh@inai.de> -----BEGIN PGP PUBLIC KEY BLOCK----- -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 -cCAy -=ePMx +mQINBF6oma8BEACXgERXgUrTVTUu1ivWCzo3zUu2VJpEFZulgSklraVs0YZLbiCl +8IKXMAM5B83ZQn8fK0NMF8nzgfOMTeiTOtb5nKUNcHAzAaVEZEpze+CK/dTERlXL +aqOBs3Q8H6vaHV5vtjtovIm+h2J5gpnDe5tFHnk3Z+COVjKm2tfTL/URbvs2qeyY +Dqf/r7rAEdoBQ29XJbQ4MMCafgiIfdL8yja0hbEKZhUeaaxNSASq8zoVyjQsu4PW +QCFYgdBGSzpWjju6zBmZdhQ016KCbOHys+6pj5n4tKJBfs7AnyFhsQA2HuiUECmN +V4S56eKstFtxmX5QARXTQelptzfW17AxhdU/2jQ1ioOD5jl9uw4NMxB89j7WHMw2 +fkKicHZaGJ6TgmUk2b86c6J2WM/77ckdxEUwVdz8iA1rMTkNVDqP74f+rZTiODbQ +sVAZupnBfFISs0Xd31ugSEq3vgA6PbXXTOiLJYgf8aHsic6PgCLGtuzIoq1W5m2D +p4raE06P8ISF2c+nawYYwD+BMlKeM9FpHYtdujc9pN2zDKmghoZYalE8Kbrcegtf +klaSc3PmOmNKdTIENLhiBTuaE878FJryl4Wtdf+tXBjEYMMftEwLlL0pIKQzxdRN +7faVX1wXiD5cFHSCEC84F29IBWJP6CCJ8dK2DOUlW4ZceVUgthLZBL0BMwARAQAB +tCBKYW4gRW5nZWxoYXJkdCA8amVuZ2VsaEBpbmFpLmRlPokCUwQTAQoAPRYhBCNo +bBCkVpG+ekJRCdY4gYHzWgk4BQJeqJmvAhsDBQkJlCYABAsJCAcFFQoJCAsFFgID +AQACHgECF4AACgkQ1jiBgfNaCTgURw/+M+R0KfuVbLUkh1PsXweGYRYnzlNQqK0x ++3dsWG2T6P89T1MBXFfnGcPjkLiUjCZC0x0Jt6W8vtaLsGdSLd81oWE1UaD4gRh2 +tLYO9gw6IheEOwlsrEHOpStYWX6mqtBGwxM222aYnXRq/0ZfCGXEfzXfH5kfucAL +E/dxNlhEcQ6sw0viIWl9Dp40Cno3DdPY5F54s87vvzyTqpW+lWwlBhj35UaCk3Um +wviTZxNBNxP3zhXur1AwE0/oKHF+Q3YTUaNSvd4u/022HGT+tEeEeRAYpd8n0gT1 +8lOcE8OhzKW0jiHZWDvqcA0+tLxpROEmA7pLR+RgMI52gyY3rfAkldzd+SLDQVhz +UGqgSLNAgPrFxUEwd6caf+9uk/CUeVv+3fv4rKul2OQ+65ahl0EYTahQkQLCNKOx +G7xAFfwDFuI5QBQPM1IudQwKcnFNgtwTwqXRJk2sqvqnDy38zkht6Y8se6cYKA6X +d+kVVlaVHs0ia8idTKczpSOhqBkqlpfn1YNVIFbXLOtRtlqu7NO0tLt0Qrf114ly +PcVYlw4f202g7sB1Xtim+kF46tqduoDmVadcDCUzB6yRQWDhmEZ3705sa2d8cQvG +BGWQILCvz/RAKJ3NhbvlDYnTxeU+W18/4Y7B42FxbAjQSe6ETnbjyII++kwM4GeL +tBjlt7M5B3i5Ag0EXqiZrwEQAOJMlaEkNofyV8l0oCfC1WwxYjsMkx37+lAlV9jz +SHwUC9wu0S2mw3rallfAhkQXJcc81AoFQpLlTwTOZZ/tBHElpVcNXQ3GeYaSwNzx +/U5YCgQa40b8xKHK2ADXVE1kRFN9b4qV5r9BSbcSw6wqqX42g5IPJuYuv7eDGneB +0ZG1cTlyOJKNBQF56UnqFvrX3XiUIwaH7O9rpNeFOyXxjqvi5V9l1PAAtWzZcAfp +IE844Bp6e4ANIJHtA+pfpTevYghmkqvaShQJbI+4qRUWGO4gSPpwqI90y0L2fH5q +wXWHUbKweZAkUGXmetjRAsAisX48X2Jf9yqX55kIkHFh07kVUaxe7fHjbzvEaUKZ +svnH8IdOoLRD6JuHiaWVIx7qaHauodjLf5DaHjMHL9djSzXZ3FKVb94FUWO2xJiU +NLCUe4ZJFb4JiPYni5ZlGOWobwnqFhWWLI/mPAIwAhMPl+ufZYYy3xG96y2PqUKp +8Kk3jKGMhBmdGSEZ4Ni81DOJFNSWOElmN6Pm6YA1AVaWBKTJTo50wnTKt7PDrFxW +tbixsqtHCawoSdKSHcqU4JEH5gaM/3nHMXvOIuNqSJOb75uhOkd+pgsJOp1a9LiP +lgNfTJ/JJpM5KD5sTzYtRLtCiyFEwjplYAkBGy2Mka2FggVu/hB3JkNxvBS0fMGJ +ixdfABEBAAGJAjsEGAEKACYWIQQjaGwQpFaRvnpCUQnWOIGB81oJOAUCXqiZrwIb +DAUJCZQmAAAKCRDWOIGB81oJOHorD/Y2O956ujqkT+QVuqFjQ2U5TriatREgIMzV +u7Vl2r+lijYuMi2gmgmJCfJEr0o8bNOmrFpbnXQDkfDir3J6mgRmnxXS1x9s9LaX ++/vxb2qHdG4xrEPJWHiKOsMK900++M9uYSiKG65QUfoaf3URVPe+OL+0AlbL5Oxj +26a7tliJ5BNMYrmO0zGYMDtL/vL6vMydem+81rT/k1Jrldpah8hvRvqGiRLW0sJN +faRMTjzD9F4oltW4pjni95/4tuf0C/oi50dMoX/yo8EDTiCP+ucHo2LoCNQGgVOw +7NegRUV7vmQejDx7SBWFcndyqTijdiu+GLBf12iBAI1MQiyMcntw0WkBPNQsubYW +FPhbIScOjlLi7iSqy3P0XfxvR4zTPrhNf8sFA1ZzaFcE3NL8e9GSMRYwzt8QaSHI +Ha1z85tFAmKegtShgcoWtVYooxAke4KZYrkDSGzKuRASkeBxIdnZZnVc74i6Fcxc +pKt43tWZftpmHNi8Tl3An3PlMBHY+APxEX5BPqhV5a0upkTn4pCgRnbKCRsYgri9 +OhifoVuTluGGN+RGKFzzlGA3OhvzXGGrGhY3ilAy6/5vk5Nj34Gv6jhDNb067yIh +8beeUVDkGzaW/CAAzUYcEarih1+BLnHEpZwADr50M+BwnyEhRhAqYOWMQaNMvucg +NGTT6x12 +=QLsa -----END PGP PUBLIC KEY BLOCK-----
participants (1)
-
root