Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/gfx/harfbuzz/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 16 kB image not shown  

Quelle  hb-subset-instancer-iup.cc   Sprache: C

 
/*
 * Copyright © 2024  Google, Inc.
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 */


#include "hb-subset-instancer-iup.hh"

/* This file is a straight port of the following:
 *
 * https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/varLib/iup.py
 *
 * Where that file returns optimzied deltas vector, we return optimized
 * referenced point indices.
 */


constexpr static unsigned MAX_LOOKBACK = 8;

static void _iup_contour_bound_forced_set (const hb_array_t<const contour_point_t> contour_points,
                                           const hb_array_t<const int> x_deltas,
                                           const hb_array_t<const int> y_deltas,
                                           hb_set_t& forced_set, /* OUT */
                                           double tolerance = 0.0)
{
  unsigned len = contour_points.length;
  unsigned next_i = 0;
  for (int i = len - 1; i >= 0; i--)
  {
    unsigned last_i = (len + i -1) % len;
    for (unsigned j = 0; j < 2; j++)
    {
      double cj, lcj, ncj;
      int dj, ldj, ndj;
      if (j == 0)
      {
        cj = static_cast<double> (contour_points.arrayZ[i].x);
        dj = x_deltas.arrayZ[i];
        lcj = static_cast<double> (contour_points.arrayZ[last_i].x);
        ldj = x_deltas.arrayZ[last_i];
        ncj = static_cast<double> (contour_points.arrayZ[next_i].x);
        ndj = x_deltas.arrayZ[next_i];
      }
      else
      {
        cj = static_cast<double> (contour_points.arrayZ[i].y);
        dj = y_deltas.arrayZ[i];
        lcj = static_cast<double> (contour_points.arrayZ[last_i].y);
        ldj = y_deltas.arrayZ[last_i];
        ncj = static_cast<double> (contour_points.arrayZ[next_i].y);
        ndj = y_deltas.arrayZ[next_i];
      }

      double c1, c2;
      int d1, d2;
      if (lcj <= ncj)
      {
        c1 = lcj;
        c2 = ncj;
        d1 = ldj;
        d2 = ndj;
      }
      else
      {
        c1 = ncj;
        c2 = lcj;
        d1 = ndj;
        d2 = ldj;
      }

      bool force = false;
      if (c1 == c2)
      {
        if (abs (d1 - d2) > tolerance && abs (dj) > tolerance)
          force = true;
      }
      else if (c1 <= cj && cj <= c2)
      {
        if (!(hb_min (d1, d2) - tolerance <= dj &&
              dj <= hb_max (d1, d2) + tolerance))
          force = true;
      }
      else
      {
        if (d1 != d2)
        {
          if (cj < c1)
          {
            if (abs (dj) > tolerance &&
                abs (dj - d1) > tolerance &&
                ((dj - tolerance < d1) != (d1 < d2)))
                force = true;
          }
          else
          {
            if (abs (dj) > tolerance &&
                abs (dj - d2) > tolerance &&
                ((d2 < dj + tolerance) != (d1 < d2)))
              force = true;
          }
        }
      }

      if (force)
      {
        forced_set.add (i);
        break;
      }
    }
    next_i = i;
  }
}

template <typename T,
          hb_enable_if (hb_is_trivially_copyable (T))>
static bool rotate_array (const hb_array_t<const T>& org_array,
                          int k,
                          hb_vector_t<T>& out)
{
  unsigned n = org_array.length;
  if (!n) return true;
  if (unlikely (!out.resize (n, false)))
    return false;

  unsigned item_size = hb_static_size (T);
  if (k < 0)
    k = n - (-k) % n;
  else
    k %= n;

  hb_memcpy ((void *) out.arrayZ, (const void *) (org_array.arrayZ + n - k), k * item_size);
  hb_memcpy ((void *) (out.arrayZ + k), (const void *) org_array.arrayZ, (n - k) * item_size);
  return true;
}

static bool rotate_set (const hb_set_t& org_set,
                        int k,
                        unsigned n,
                        hb_set_t& out)
{
  if (!n) return false;
  k %= n;
  if (k < 0)
    k = n + k;

  if (k == 0)
  {
    out.set (org_set);
  }
  else
  {
    for (auto v : org_set)
      out.add ((v + k) % n);
  }
  return !out.in_error ();
}

/* Given two reference coordinates (start and end of contour_points array),
 * output interpolated deltas for points in between */

static bool _iup_segment (const hb_array_t<const contour_point_t> contour_points,
                          const hb_array_t<const int> x_deltas,
                          const hb_array_t<const int> y_deltas,
                          const contour_point_t& p1, const contour_point_t& p2,
                          int p1_dx, int p2_dx,
                          int p1_dy, int p2_dy,
                          hb_vector_t<double>& interp_x_deltas, /* OUT */
                          hb_vector_t<double>& interp_y_deltas /* OUT */)
{
  unsigned n = contour_points.length;
  if (unlikely (!interp_x_deltas.resize (n, false) ||
                !interp_y_deltas.resize (n, false)))
    return false;

  for (unsigned j = 0; j < 2; j++)
  {
    double x1, x2, d1, d2;
    double *out;
    if (j == 0)
    {
      x1 = static_cast<double> (p1.x);
      x2 = static_cast<double> (p2.x);
      d1 = p1_dx;
      d2 = p2_dx;
      out = interp_x_deltas.arrayZ;
    }
    else
    {
      x1 = static_cast<double> (p1.y);
      x2 = static_cast<double> (p2.y);
      d1 = p1_dy;
      d2 = p2_dy;
      out = interp_y_deltas.arrayZ;
    }

    if (x1 == x2)
    {
      if (d1 == d2)
      {
        for (unsigned i = 0; i < n; i++)
          out[i] = d1;
      }
      else
      {
        for (unsigned i = 0; i < n; i++)
          out[i] = 0.0;
      }
      continue;
    }

    if (x1 > x2)
    {
      hb_swap (x1, x2);
      hb_swap (d1, d2);
    }

    double scale = (d2 - d1) / (x2 - x1);
    for (unsigned i = 0; i < n; i++)
    {
      double x = (j == 0 ? static_cast<double> (contour_points.arrayZ[i].x) : static_cast<double> (contour_points.arrayZ[i].y));
      double d;
      if (x <= x1)
        d = d1;
      else if (x >= x2)
        d = d2;
      else
        d = d1 + (x - x1) * scale;

      out[i] = d;
    }
  }
  return true;
}

static bool _can_iup_in_between (const hb_array_t<const contour_point_t> contour_points,
                                 const hb_array_t<const int> x_deltas,
                                 const hb_array_t<const int> y_deltas,
                                 const contour_point_t& p1, const contour_point_t& p2,
                                 int p1_dx, int p2_dx,
                                 int p1_dy, int p2_dy,
                                 double tolerance)
{
  hb_vector_t<double> interp_x_deltas, interp_y_deltas;
  if (!_iup_segment (contour_points, x_deltas, y_deltas,
                     p1, p2, p1_dx, p2_dx, p1_dy, p2_dy,
                     interp_x_deltas, interp_y_deltas))
    return false;

  unsigned num = contour_points.length;

  for (unsigned i = 0; i < num; i++)
  {
    double dx = static_cast<double> (x_deltas.arrayZ[i]) - interp_x_deltas.arrayZ[i];
    double dy = static_cast<double> (y_deltas.arrayZ[i]) - interp_y_deltas.arrayZ[i];
  
    if (sqrt (dx * dx + dy * dy) > tolerance)
      return false;
  }
  return true;
}

static bool _iup_contour_optimize_dp (const contour_point_vector_t& contour_points,
                                      const hb_vector_t<int>& x_deltas,
                                      const hb_vector_t<int>& y_deltas,
                                      const hb_set_t& forced_set,
                                      double tolerance,
                                      unsigned lookback,
                                      hb_vector_t<unsigned>& costs, /* OUT */
                                      hb_vector_t<int>& chain /* OUT */)
{
  unsigned n = contour_points.length;
  if (unlikely (!costs.resize (n, false) ||
                !chain.resize (n, false)))
    return false;

  lookback = hb_min (lookback, MAX_LOOKBACK);

  for (unsigned i = 0; i < n; i++)
  {
    unsigned best_cost = (i == 0 ? 1 : costs.arrayZ[i-1] + 1);
    
    costs.arrayZ[i] = best_cost;
    chain.arrayZ[i] = (i == 0 ? -1 : i - 1);

    if (i > 0 && forced_set.has (i - 1))
      continue;

    int lookback_index = hb_max ((int) i - (int) lookback + 1, -1);
    for (int j = i - 2; j >= lookback_index; j--)
    {
      unsigned cost = j == -1 ? 1 : costs.arrayZ[j] + 1;
      /* num points between i and j */
      unsigned num_points = i - j - 1;
      unsigned p1 = (j == -1 ? n - 1 : j);
      if (cost < best_cost &&
          _can_iup_in_between (contour_points.as_array ().sub_array (j + 1, num_points),
                               x_deltas.as_array ().sub_array (j + 1, num_points),
                               y_deltas.as_array ().sub_array (j + 1, num_points),
                               contour_points.arrayZ[p1], contour_points.arrayZ[i],
                               x_deltas.arrayZ[p1], x_deltas.arrayZ[i],
                               y_deltas.arrayZ[p1], y_deltas.arrayZ[i],
                               tolerance))
      {
        best_cost = cost;
        costs.arrayZ[i] = best_cost;
        chain.arrayZ[i] = j;
      }

      if (j > 0 && forced_set.has (j))
        break;
    }
  }
  return true;
}

static bool _iup_contour_optimize (const hb_array_t<const contour_point_t> contour_points,
                                   const hb_array_t<const int> x_deltas,
                                   const hb_array_t<const int> y_deltas,
                                   hb_array_t<bool> opt_indices, /* OUT */
                                   double tolerance = 0.0)
{
  unsigned n = contour_points.length;
  if (opt_indices.length != n ||
      x_deltas.length != n ||
      y_deltas.length != n)
    return false;

  bool all_within_tolerance = true;
  for (unsigned i = 0; i < n; i++)
  {
    int dx = x_deltas.arrayZ[i];
    int dy = y_deltas.arrayZ[i];
    if (sqrt ((double) dx * dx + (double) dy * dy) > tolerance)
    {
      all_within_tolerance = false;
      break;
    }
  }

  /* If all are within tolerance distance, do nothing, opt_indices is
   * initilized to false */

  if (all_within_tolerance)
    return true;

  /* If there's exactly one point, return it */
  if (n == 1)
  {
    opt_indices.arrayZ[0] = true;
    return true;
  }

  /* If all deltas are exactly the same, return just one (the first one) */
  bool all_deltas_are_equal = true;
  for (unsigned i = 1; i < n; i++)
    if (x_deltas.arrayZ[i] != x_deltas.arrayZ[0] ||
        y_deltas.arrayZ[i] != y_deltas.arrayZ[0])
    {
      all_deltas_are_equal = false;
      break;
    }

  if (all_deltas_are_equal)
  {
    opt_indices.arrayZ[0] = true;
    return true;
  }

  /* else, solve the general problem using Dynamic Programming */
  hb_set_t forced_set;
  _iup_contour_bound_forced_set (contour_points, x_deltas, y_deltas, forced_set, tolerance);

  if (!forced_set.is_empty ())
  {
    int k = n - 1 - forced_set.get_max ();
    if (k < 0)
      return false;

    hb_vector_t<int> rot_x_deltas, rot_y_deltas;
    contour_point_vector_t rot_points;
    hb_set_t rot_forced_set;
    if (!rotate_array (contour_points, k, rot_points) ||
        !rotate_array (x_deltas, k, rot_x_deltas) ||
        !rotate_array (y_deltas, k, rot_y_deltas) ||
        !rotate_set (forced_set, k, n, rot_forced_set))
      return false;

    hb_vector_t<unsigned> costs;
    hb_vector_t<int> chain;

    if (!_iup_contour_optimize_dp (rot_points, rot_x_deltas, rot_y_deltas,
                                   rot_forced_set, tolerance, n,
                                   costs, chain))
      return false;

    hb_set_t solution;
    int index = n - 1;
    while (index != -1)
    {
      solution.add (index);
      index = chain.arrayZ[index];
    }

    if (solution.is_empty () ||
        forced_set.get_population () > solution.get_population ())
      return false;

    for (unsigned i : solution)
      opt_indices.arrayZ[i] = true;

    hb_vector_t<bool> rot_indices;
    const hb_array_t<const bool> opt_indices_array (opt_indices.arrayZ, opt_indices.length);
    rotate_array (opt_indices_array, -k, rot_indices);

    for (unsigned i = 0; i < n; i++)
      opt_indices.arrayZ[i] = rot_indices.arrayZ[i];
  }
  else
  {
    hb_vector_t<int> repeat_x_deltas, repeat_y_deltas;
    contour_point_vector_t repeat_points;

    if (unlikely (!repeat_x_deltas.resize (n * 2, false) ||
                  !repeat_y_deltas.resize (n * 2, false) ||
                  !repeat_points.resize (n * 2, false)))
      return false;

    unsigned contour_point_size = hb_static_size (contour_point_t);
    for (unsigned i = 0; i < n; i++)
    {
      hb_memcpy ((void *) repeat_x_deltas.arrayZ, (const void *) x_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));
      hb_memcpy ((void *) (repeat_x_deltas.arrayZ + n), (const void *) x_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));

      hb_memcpy ((void *) repeat_y_deltas.arrayZ, (const void *) y_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));
      hb_memcpy ((void *) (repeat_y_deltas.arrayZ + n), (const void *) y_deltas.arrayZ, n * sizeof (repeat_x_deltas[0]));

      hb_memcpy ((void *) repeat_points.arrayZ, (const void *) contour_points.arrayZ, n * contour_point_size);
      hb_memcpy ((void *) (repeat_points.arrayZ + n), (const void *) contour_points.arrayZ, n * contour_point_size);
    }

    hb_vector_t<unsigned> costs;
    hb_vector_t<int> chain;
    if (!_iup_contour_optimize_dp (repeat_points, repeat_x_deltas, repeat_y_deltas,
                                   forced_set, tolerance, n,
                                   costs, chain))
      return false;

    unsigned best_cost = n + 1;
    int len = costs.length;
    hb_set_t best_sol;
    for (int start = n - 1; start < len; start++)
    {
      hb_set_t solution;
      int i = start;
      int lookback = start - (int) n;
      while (i > lookback)
      {
        solution.add (i % n);
        i = chain.arrayZ[i];
      }
      if (i == lookback)
      {
        unsigned cost_i = i < 0 ? 0 : costs.arrayZ[i];
        unsigned cost = costs.arrayZ[start] - cost_i;
        if (cost <= best_cost)
        {
          best_sol.set (solution);
          best_cost = cost;
        }
      }
    }

    for (unsigned i = 0; i < n; i++)
      if (best_sol.has (i))
        opt_indices.arrayZ[i] = true;
  }
  return true;
}

bool iup_delta_optimize (const contour_point_vector_t& contour_points,
                         const hb_vector_t<int>& x_deltas,
                         const hb_vector_t<int>& y_deltas,
                         hb_vector_t<bool>& opt_indices, /* OUT */
                         double tolerance)
{
  if (!opt_indices.resize (contour_points.length))
      return false;

  hb_vector_t<unsigned> end_points;
  unsigned count = contour_points.length;
  if (unlikely (!end_points.alloc (count)))
    return false;

  for (unsigned i = 0; i < count - 4; i++)
    if (contour_points.arrayZ[i].is_end_point)
      end_points.push (i);

  /* phantom points */
  for (unsigned i = count - 4; i < count; i++)
    end_points.push (i);

  if (end_points.in_error ()) return false;

  unsigned start = 0;
  for (unsigned end : end_points)
  {
    unsigned len = end - start + 1;
    if (!_iup_contour_optimize (contour_points.as_array ().sub_array (start, len),
                                x_deltas.as_array ().sub_array (start, len),
                                y_deltas.as_array ().sub_array (start, len),
                                opt_indices.as_array ().sub_array (start, len),
                                tolerance))
      return false;
    start = end + 1;
  }
  return true;
}

Messung V0.5
C=98 H=92 G=94

¤ Dauer der Verarbeitung: 0.32 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.