Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/harfbuzz/src/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 31 kB image not shown  

Quelle  hb-gpu-draw.cc   Sprache: C

 

/*
 * Copyright (C) 2026  Behdad Esfahbod
 *
 *  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.
 *
 * Author(s): Behdad Esfahbod
 */


#include "hb.hh"

#include "hb-gpu.h"
#include "hb-gpu-draw.hh"
#include "hb-gpu-cu2qu.hh"
#include "hb-machinery.hh"

#include <cmath>

/* ---- Accumulator ---- */

static void
acc_update_extents (hb_gpu_draw_t *g, double x, double y)
{
  g->ext_min_x = hb_min (g->ext_min_x, x);
  g->ext_min_y = hb_min (g->ext_min_y, y);
  g->ext_max_x = hb_max (g->ext_max_x, x);
  g->ext_max_y = hb_max (g->ext_max_y, y);
}

static void
acc_emit (hb_gpu_draw_t *g,
   bool contour_start,
   double p1x, double p1y,
   double p2x, double p2y,
   double p3x, double p3y)
{
  if (unlikely (g->num_curves >= HB_GPU_DRAW_MAX_CURVES))
  {
    g->success = false;
    return;
  }

  hb_gpu_curve_t c = {p1x, p1y, p2x, p2y, p3x, p3y, contour_start};
  if (unlikely (!g->curves.push_or_fail (c)))
  {
    g->success = false;
    return;
  }

  g->num_curves++;
  g->current_x = p3x;
  g->current_y = p3y;

  acc_update_extents (g, p1x, p1y);
  acc_update_extents (g, p2x, p2y);
  acc_update_extents (g, p3x, p3y);
}

static void
acc_emit_conic (hb_gpu_draw_t *g,
  double cx, double cy,
  double x, double y)
{
  if (g->current_x == x && g->current_y == y)
    return;

  bool contour_start = g->need_moveto;
  if (g->need_moveto) {
    g->start_x = g->current_x;
    g->start_y = g->current_y;
    g->need_moveto = false;
  }

  acc_emit (g, contour_start,
     g->current_x, g->current_y, cx, cy, x, y);
}

void
hb_gpu_draw_t::acc_move_to (double x, double y)
{
  if (unlikely (!success))
    return;

  need_moveto = true;
  current_x = x;
  current_y = y;
}

void
hb_gpu_draw_t::acc_line_to (double x, double y)
{
  if (unlikely (!success))
    return;

  acc_emit_conic (this, current_x, current_y, x, y);
}

void
hb_gpu_draw_t::acc_conic_to (double cx, double cy, double x, double y)
{
  if (unlikely (!success))
    return;

  acc_emit_conic (this, cx, cy, x, y);
}

void
hb_gpu_draw_t::acc_close_path ()
{
  if (unlikely (!success))
    return;

  if (!need_moveto &&
      (current_x != start_x || current_y != start_y))
    acc_line_to (start_x, start_y);
}


void
hb_gpu_draw_t::acc_cubic_to (double c1x, double c1y,
          double c2x, double c2y,
          double x, double y)
{
  if (unlikely (!success))
    return;

  double c0x = current_x, c0y = current_y;

  if (c0x == x && c0y == y &&
      c1x == x && c1y == y &&
      c2x == x && c2y == y)
    return;

  /* Degenerate cubic: all control points are collinear.
   * Emit as a line instead of running cu2qu subdivision. */

  double dx = x - c0x, dy = y - c0y;
  double len_sq = dx * dx + dy * dy;
  if (len_sq > 0.)
  {
    double inv = 1.0 / len_sq;
    double cross1 = (c1x - c0x) * dy - (c1y - c0y) * dx;
    double cross2 = (c2x - c0x) * dy - (c2y - c0y) * dx;
    if (cross1 * cross1 * inv <= HB_GPU_CU2QU_TOLERANCE * HB_GPU_CU2QU_TOLERANCE &&
 cross2 * cross2 * inv <= HB_GPU_CU2QU_TOLERANCE * HB_GPU_CU2QU_TOLERANCE)
    {
      acc_line_to (x, y);
      return;
    }
  }

  hb_gpu_cubic_to_quadratics (this,
         {c0x, c0y}, {c1x, c1y},
         {c2x, c2y}, {x, y},
         HB_GPU_CU2QU_TOLERANCE, 0);
}


/* ---- Draw funcs ---- */

static void
hb_gpu_draw_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
       void *data,
       hb_draw_state_t *st HB_UNUSED,
       float to_x, float to_y,
       void *user_data HB_UNUSED)
{
  hb_gpu_draw_t *g = (hb_gpu_draw_t *) data;
  g->acc_close_path ();
  g->acc_move_to ((double) to_x, (double) to_y);
}

static void
hb_gpu_draw_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
       void *data,
       hb_draw_state_t *st HB_UNUSED,
       float to_x, float to_y,
       void *user_data HB_UNUSED)
{
  hb_gpu_draw_t *g = (hb_gpu_draw_t *) data;
  g->acc_line_to ((double) to_x, (double) to_y);
}

static void
hb_gpu_draw_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
     void *data,
     hb_draw_state_t *st HB_UNUSED,
     float control_x, float control_y,
     float to_x, float to_y,
     void *user_data HB_UNUSED)
{
  hb_gpu_draw_t *g = (hb_gpu_draw_t *) data;
  g->acc_conic_to ((double) control_x, (double) control_y,
     (double) to_x, (double) to_y);
}

static void
hb_gpu_draw_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
        void *data,
        hb_draw_state_t *st HB_UNUSED,
        float control1_x, float control1_y,
        float control2_x, float control2_y,
        float to_x, float to_y,
        void *user_data HB_UNUSED)
{
  hb_gpu_draw_t *g = (hb_gpu_draw_t *) data;
  g->acc_cubic_to ((double) control1_x, (double) control1_y,
     (double) control2_x, (double) control2_y,
     (double) to_x, (double) to_y);
}

static void
hb_gpu_draw_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED,
   void *data,
   hb_draw_state_t *st HB_UNUSED,
   void *user_data HB_UNUSED)
{
  hb_gpu_draw_t *g = (hb_gpu_draw_t *) data;
  g->acc_close_path ();
}

static inline void free_static_gpu_draw_funcs ();

static struct hb_gpu_draw_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t<hb_gpu_draw_funcs_lazy_loader_t>
{
  static hb_draw_funcs_t *create ()
  {
    hb_draw_funcs_t *funcs = hb_draw_funcs_create ();

    hb_draw_funcs_set_move_to_func      (funcs, hb_gpu_draw_move_to,      nullptr, nullptr);
    hb_draw_funcs_set_line_to_func      (funcs, hb_gpu_draw_line_to,      nullptr, nullptr);
    hb_draw_funcs_set_quadratic_to_func (funcs, hb_gpu_draw_quadratic_to, nullptr, nullptr);
    hb_draw_funcs_set_cubic_to_func     (funcs, hb_gpu_draw_cubic_to,     nullptr, nullptr);
    hb_draw_funcs_set_close_path_func   (funcs, hb_gpu_draw_close_path,   nullptr, nullptr);

    hb_draw_funcs_make_immutable (funcs);

    hb_atexit (free_static_gpu_draw_funcs);

    return funcs;
  }
} static_gpu_draw_funcs;

static inline void
free_static_gpu_draw_funcs ()
{
  static_gpu_draw_funcs.free_instance ();
}


/* ---- Encode ---- */

struct hb_gpu_texel_t
{
  int16_t r, g, b, a;
};

static hb_position_t
clamp_to_hb_position (double v)
{
  return (hb_position_t) hb_clamp (v,
       (double) hb_int_min (hb_position_t),
       (double) hb_int_max (hb_position_t));
}

static inline int16_t
quantize (double v)
{
  return (int16_t) round (v * HB_GPU_UNITS_PER_EM);
}

static inline int16_t
quantize_down (double v)
{
  return (int16_t) floor (v * HB_GPU_UNITS_PER_EM);
}

static inline int16_t
quantize_up (double v)
{
  return (int16_t) ceil (v * HB_GPU_UNITS_PER_EM);
}

static inline double
dequantize (int16_t v)
{
  return (double) v / HB_GPU_UNITS_PER_EM;
}

static inline bool
quantize_down_fits_i16 (double v)
{
  double q = floor (v * HB_GPU_UNITS_PER_EM);
  return q >= INT16_MIN &&
  q <= INT16_MAX;
}

static inline bool
quantize_up_fits_i16 (double v)
{
  double q = ceil (v * HB_GPU_UNITS_PER_EM);
  return q >= INT16_MIN &&
  q <= INT16_MAX;
}

static inline int16_t
encode_offset (unsigned offset)
{
  return (int16_t) (offset - 32768u);
}

static hb_gpu_encode_curve_info_t
encode_curve_info (const hb_gpu_curve_t *c)
{
  hb_gpu_encode_curve_info_t info;

  info.min_x = hb_min (hb_min (c->p1x, c->p2x), c->p3x);
  info.max_x = hb_max (hb_max (c->p1x, c->p2x), c->p3x);
  info.min_y = hb_min (hb_min (c->p1y, c->p2y), c->p3y);
  info.max_y = hb_max (hb_max (c->p1y, c->p2y), c->p3y);
  info.is_horizontal = c->p1y == c->p2y && c->p2y == c->p3y;
  info.is_vertical   = c->p1x == c->p2x && c->p2x == c->p3x;
  info.hband_lo = 0;
  info.hband_hi = -1;
  info.vband_lo = 0;
  info.vband_hi = -1;

  return info;
}

static void
_hb_gpu_draw_get_extents (hb_gpu_draw_t      *draw,
     hb_glyph_extents_t *extents)
{
  if (unlikely (!draw->success) ||
      draw->num_curves == 0 ||
      draw->ext_min_x == HUGE_VAL)
  {
    extents->x_bearing = 0;
    extents->y_bearing = 0;
    extents->width = 0;
    extents->height = 0;
    return;
  }

  double min_x = floor (draw->ext_min_x);
  double min_y = floor (draw->ext_min_y);
  double max_x = ceil  (draw->ext_max_x);
  double max_y = ceil  (draw->ext_max_y);

  if (unlikely (!std::isfinite (min_x) ||
  !std::isfinite (min_y) ||
  !std::isfinite (max_x) ||
  !std::isfinite (max_y)))
  {
    extents->x_bearing = 0;
    extents->y_bearing = 0;
    extents->width = 0;
    extents->height = 0;
    return;
  }

  extents->x_bearing = clamp_to_hb_position (min_x);
  extents->y_bearing = clamp_to_hb_position (max_y);
  extents->width     = clamp_to_hb_position (max_x - min_x);
  extents->height    = clamp_to_hb_position (min_y - max_y);
}


/**
 * hb_gpu_draw_encode:
 * @draw: a GPU shape encoder
 * @extents: (out) (nullable): where to store the computed glyph
 *           extents (in font units, Y-up).  Pass `NULL` if not
 *           needed.
 *
 * Encodes the accumulated outlines into a compact blob
 * suitable for GPU rendering.  The blob data is an array of
 * RGBA16I texels (8 bytes each) to be uploaded to a texture
 * buffer object.
 *
 * The returned blob owns its own copy of the data.  On success
 * @draw is auto-cleared so it can be reused for the next glyph;
 * user configuration (font scale) is preserved.
 *
 * Return value: (transfer full):
 * An #hb_blob_t containing the encoded data, or `NULL` if encoding
 * failed (allocation failure or accumulation error).  When the
 * encoder accumulated no outline (e.g. the glyph has no ink, like a
 * space), returns the empty-blob singleton instead of `NULL`, so
 * callers can distinguish "nothing to render" (length 0) from a
 * real failure (`NULL`).
 *
 * Since: 14.0.0
 **/

hb_blob_t *
hb_gpu_draw_encode (hb_gpu_draw_t      *draw,
                    hb_glyph_extents_t *extents)
{
  /* Capture computed extents before auto-clear wipes them. */
  if (extents)
    _hb_gpu_draw_get_extents (draw, extents);
  HB_SCOPE_GUARD (hb_gpu_draw_clear (draw));

  if (unlikely (!draw->success))
    return nullptr;

  const hb_gpu_curve_t *curves = draw->curves.arrayZ;
  unsigned num_curves = draw->curves.length;

  if (num_curves == 0)
    return hb_blob_get_empty ();

  hb_gpu_encode_scratch_t &s = draw->scratch;

  s.curve_infos.reset_if_error ();
  s.hband_curve_counts.reset_if_error ();
  s.vband_curve_counts.reset_if_error ();
  s.hband_offsets.reset_if_error ();
  s.vband_offsets.reset_if_error ();
  s.hband_curves.reset_if_error ();
  s.hband_curves_asc.reset_if_error ();
  s.vband_curves.reset_if_error ();
  s.vband_curves_asc.reset_if_error ();
  s.hband_cursors.reset_if_error ();
  s.vband_cursors.reset_if_error ();
  s.curve_texel_offset.reset_if_error ();

  /* Compute per-curve info and extents */
  if (unlikely (!s.curve_infos.resize (num_curves)))
    return nullptr;

  int16_t min_x_q = quantize_down (draw->ext_min_x);
  int16_t min_y_q = quantize_down (draw->ext_min_y);
  int16_t max_x_q = quantize_up (draw->ext_max_x);
  int16_t max_y_q = quantize_up (draw->ext_max_y);

  double min_x = dequantize (min_x_q);
  double min_y = dequantize (min_y_q);
  double max_x = dequantize (max_x_q);
  double max_y = dequantize (max_y_q);

  for (unsigned i = 0; i < num_curves; i++)
    s.curve_infos.arrayZ[i] = encode_curve_info (&curves[i]);

  /* Choose number of bands (capped at 16 per Slug paper) */
  unsigned num_hbands = hb_min (num_curves, 16u);
  unsigned num_vbands = hb_min (num_curves, 16u);
  num_hbands = hb_max (num_hbands, 1u);
  num_vbands = hb_max (num_vbands, 1u);

  double height = max_y - min_y;
  double width  = max_x - min_x;

  if (height <= 0) num_hbands = 1;
  if (width  <= 0) num_vbands = 1;

  double hband_size = height / num_hbands;
  double vband_size = width  / num_vbands;

  if (unlikely (!s.hband_curve_counts.resize (num_hbands) ||
  !s.vband_curve_counts.resize (num_vbands)))
    return nullptr;

  for (unsigned b = 0; b < num_hbands; b++) s.hband_curve_counts.arrayZ[b] = 0;
  for (unsigned b = 0; b < num_vbands; b++) s.vband_curve_counts.arrayZ[b] = 0;

  for (unsigned i = 0; i < num_curves; i++)
  {
    hb_gpu_encode_curve_info_t &info = s.curve_infos.arrayZ[i];

    if (!info.is_horizontal)
    {
      if (height > 0) {
 info.hband_lo = (int) floor ((info.min_y - min_y) / hband_size);
 info.hband_hi = (int) floor ((info.max_y - min_y) / hband_size);
 info.hband_lo = hb_max (info.hband_lo, 0);
 info.hband_hi = hb_min (info.hband_hi, (int) num_hbands - 1);
 for (int b = info.hband_lo; b <= info.hband_hi; b++)
   s.hband_curve_counts.arrayZ[b]++;
      } else {
 info.hband_lo = 0;
 info.hband_hi = 0;
 s.hband_curve_counts.arrayZ[0]++;
      }
    }

    if (!info.is_vertical)
    {
      if (width > 0) {
 info.vband_lo = (int) floor ((info.min_x - min_x) / vband_size);
 info.vband_hi = (int) floor ((info.max_x - min_x) / vband_size);
 info.vband_lo = hb_max (info.vband_lo, 0);
 info.vband_hi = hb_min (info.vband_hi, (int) num_vbands - 1);
 for (int b = info.vband_lo; b <= info.vband_hi; b++)
   s.vband_curve_counts.arrayZ[b]++;
      } else {
 info.vband_lo = 0;
 info.vband_hi = 0;
 s.vband_curve_counts.arrayZ[0]++;
      }
    }
  }

  if (unlikely (!s.hband_offsets.resize (num_hbands) ||
  !s.vband_offsets.resize (num_vbands)))
    return nullptr;

  unsigned total_hband_indices = 0;
  unsigned total_vband_indices = 0;

  for (unsigned b = 0; b < num_hbands; b++) {
    s.hband_offsets.arrayZ[b] = total_hband_indices;
    if (unlikely (hb_unsigned_add_overflows (total_hband_indices,
          s.hband_curve_counts.arrayZ[b],
          &total_hband_indices)))
      return nullptr;
  }

  for (unsigned b = 0; b < num_vbands; b++) {
    s.vband_offsets.arrayZ[b] = total_vband_indices;
    if (unlikely (hb_unsigned_add_overflows (total_vband_indices,
          s.vband_curve_counts.arrayZ[b],
          &total_vband_indices)))
      return nullptr;
  }

  /* Assign curves to bands */
  if (unlikely (!s.hband_curves.resize (total_hband_indices) ||
  !s.hband_curves_asc.resize (total_hband_indices) ||
  !s.vband_curves.resize (total_vband_indices) ||
  !s.vband_curves_asc.resize (total_vband_indices) ||
  !s.hband_cursors.resize (num_hbands) ||
  !s.vband_cursors.resize (num_vbands)))
    return nullptr;

  for (unsigned b = 0; b < num_hbands; b++) s.hband_cursors.arrayZ[b] = s.hband_offsets.arrayZ[b];
  for (unsigned b = 0; b < num_vbands; b++) s.vband_cursors.arrayZ[b] = s.vband_offsets.arrayZ[b];

  for (unsigned i = 0; i < num_curves; i++)
  {
    const hb_gpu_encode_curve_info_t &info = s.curve_infos.arrayZ[i];

    for (int b = info.hband_lo; b <= info.hband_hi; b++) {
      unsigned idx = s.hband_cursors.arrayZ[b]++;
      s.hband_curves.arrayZ[idx] = i;
      s.hband_curves_asc.arrayZ[idx] = i;
    }

    for (int b = info.vband_lo; b <= info.vband_hi; b++) {
      unsigned idx = s.vband_cursors.arrayZ[b]++;
      s.vband_curves.arrayZ[idx] = i;
      s.vband_curves_asc.arrayZ[idx] = i;
    }
  }

  /* Sort: descending by max, ascending by min */
  const hb_gpu_encode_curve_info_t *infos = s.curve_infos.arrayZ;

  for (unsigned b = 0; b < num_hbands; b++)
  {
    unsigned off = s.hband_offsets.arrayZ[b];
    unsigned count = s.hband_curve_counts.arrayZ[b];
    s.hband_curves.as_array ().sub_array (off, count)
      .qsort ([infos] (const unsigned &a, const unsigned &b) {
 return infos[a].max_x > infos[b].max_x;
      });
    s.hband_curves_asc.as_array ().sub_array (off, count)
      .qsort ([infos] (const unsigned &a, const unsigned &b) {
 return infos[a].min_x < infos[b].min_x;
      });
  }

  for (unsigned b = 0; b < num_vbands; b++)
  {
    unsigned off = s.vband_offsets.arrayZ[b];
    unsigned count = s.vband_curve_counts.arrayZ[b];
    s.vband_curves.as_array ().sub_array (off, count)
      .qsort ([infos] (const unsigned &a, const unsigned &b) {
 return infos[a].max_y > infos[b].max_y;
      });
    s.vband_curves_asc.as_array ().sub_array (off, count)
      .qsort ([infos] (const unsigned &a, const unsigned &b) {
 return infos[a].min_y < infos[b].min_y;
      });
  }

  /* Compute sizes */
  unsigned total_curve_indices;
  if (unlikely (hb_unsigned_add_overflows (total_hband_indices,
        total_vband_indices,
        &total_curve_indices) ||
  hb_unsigned_mul_overflows (total_curve_indices,
        2,
        &total_curve_indices)))
    return nullptr;

  unsigned header_len = 2;

  unsigned num_contour_breaks = 0;
  for (unsigned i = 0; i + 1 < num_curves; i++)
    if (curves[i + 1].contour_start)
      num_contour_breaks++;

  unsigned curve_data_len;
  if (unlikely (hb_unsigned_add_overflows (num_curves,
        num_contour_breaks,
        &curve_data_len) ||
  hb_unsigned_add_overflows (curve_data_len,
        1,
        &curve_data_len)))
    return nullptr;

  unsigned band_headers_len;
  if (unlikely (hb_unsigned_add_overflows (num_hbands,
        num_vbands,
        &band_headers_len)))
    return nullptr;

  unsigned total_len = header_len;
  if (unlikely (hb_unsigned_add_overflows (total_len,
        band_headers_len,
        &total_len) ||
  hb_unsigned_add_overflows (total_len,
        total_curve_indices,
        &total_len) ||
  hb_unsigned_add_overflows (total_len,
        curve_data_len,
        &total_len)))
    return nullptr;

  /* Validate fits in uint16 offsets (stored as int16 with bias) */
  if (total_len > (unsigned) UINT16_MAX + 1u)
    return nullptr;

  if (!quantize_down_fits_i16 (draw->ext_min_x) ||
      !quantize_down_fits_i16 (draw->ext_min_y) ||
      !quantize_up_fits_i16 (draw->ext_max_x) ||
      !quantize_up_fits_i16 (draw->ext_max_y))
    return nullptr;

  /* Allocate or reuse encode buffer */
  unsigned needed_bytes;
  if (unlikely (hb_unsigned_mul_overflows (total_len,
        sizeof (hb_gpu_texel_t),
        &needed_bytes)))
    return nullptr;
  unsigned buf_capacity = 0;
  char *replaced_recycled_buf = nullptr;
  char *buf_raw = hb_blob_t::recycle_acquire (draw->recycled_blob, needed_bytes,
     &buf_capacity, &replaced_recycled_buf);
  if (unlikely (!buf_raw))
    return nullptr;
  hb_gpu_texel_t *buf = (hb_gpu_texel_t *) (void *) buf_raw;

  unsigned curve_data_offset = header_len;
  if (unlikely (hb_unsigned_add_overflows (curve_data_offset,
        band_headers_len,
        &curve_data_offset) ||
  hb_unsigned_add_overflows (curve_data_offset,
        total_curve_indices,
        &curve_data_offset)))
  {
    hb_blob_t::recycle_abort ((char *) buf, draw->recycled_blob);
    return nullptr;
  }

  /* Pack header */
  buf[0].r = min_x_q;
  buf[0].g = min_y_q;
  buf[0].b = max_x_q;
  buf[0].a = max_y_q;
  buf[1].r = (int16_t) num_hbands;
  buf[1].g = (int16_t) num_vbands;
  buf[1].b = (int16_t) hb_clamp (draw->x_scale, -32768, 32767);
  buf[1].a = (int16_t) hb_clamp (draw->y_scale, -32768, 32767);

  /* Pack curve data with shared endpoints */
  if (unlikely (!s.curve_texel_offset.resize (num_curves)))
  {
    hb_blob_t::recycle_abort ((char *) buf, draw->recycled_blob);
    return nullptr;
  }

  unsigned texel = curve_data_offset;

  for (unsigned i = 0; i < num_curves; i++)
  {
    bool contour_start = curves[i].contour_start;

    if (contour_start) {
      s.curve_texel_offset.arrayZ[i] = texel;
      buf[texel].r = quantize (curves[i].p1x);
      buf[texel].g = quantize (curves[i].p1y);
      buf[texel].b = quantize (curves[i].p2x);
      buf[texel].a = quantize (curves[i].p2y);
      texel++;
    } else {
      s.curve_texel_offset.arrayZ[i] = texel - 1;
    }

    bool has_next = i + 1 < num_curves &&
      !curves[i + 1].contour_start;

    buf[texel].r = quantize (curves[i].p3x);
    buf[texel].g = quantize (curves[i].p3y);
    if (has_next) {
      buf[texel].b = quantize (curves[i + 1].p2x);
      buf[texel].a = quantize (curves[i + 1].p2y);
    } else {
      buf[texel].b = 0;
      buf[texel].a = 0;
    }
    texel++;
  }

  /* Pack band headers and curve indices */
  unsigned index_offset = header_len + band_headers_len;

  for (unsigned b = 0; b < num_hbands; b++)
  {
    int16_t hband_split;
    {
      unsigned off = s.hband_offsets.arrayZ[b];
      unsigned n = s.hband_curve_counts.arrayZ[b];
      unsigned best_worst = n;
      double best_split = (min_x + max_x) * 0.5;
      unsigned left_count = n;
      for (unsigned ci = 0; ci < n; ci++) {
 double split = s.curve_infos.arrayZ[s.hband_curves.arrayZ[off + ci]].max_x;
 unsigned right_count = ci + 1;
 while (left_count &&
        s.curve_infos.arrayZ[s.hband_curves_asc.arrayZ[off + left_count - 1]].min_x > split)
   left_count--;
 unsigned worst = hb_max (right_count, left_count);
 if (worst < best_worst) {
   best_worst = worst;
   best_split = split;
 }
      }
      hband_split = quantize (best_split);
    }
    unsigned hdr = header_len + b;
    unsigned desc_off = index_offset;

    for (unsigned ci = 0; ci < s.hband_curve_counts.arrayZ[b]; ci++) {
      buf[index_offset].r = encode_offset (s.curve_texel_offset.arrayZ[s.hband_curves.arrayZ[s.hband_offsets.arrayZ[b] + ci]]);
      buf[index_offset].g = 0;
      buf[index_offset].b = 0;
      buf[index_offset].a = 0;
      index_offset++;
    }

    unsigned asc_off = index_offset;

    for (unsigned ci = 0; ci < s.hband_curve_counts.arrayZ[b]; ci++) {
      buf[index_offset].r = encode_offset (s.curve_texel_offset.arrayZ[s.hband_curves_asc.arrayZ[s.hband_offsets.arrayZ[b] + ci]]);
      buf[index_offset].g = 0;
      buf[index_offset].b = 0;
      buf[index_offset].a = 0;
      index_offset++;
    }

    buf[hdr].r = (int16_t) s.hband_curve_counts.arrayZ[b];
    buf[hdr].g = encode_offset (desc_off);
    buf[hdr].b = encode_offset (asc_off);
    buf[hdr].a = hband_split;
  }

  for (unsigned b = 0; b < num_vbands; b++)
  {
    int16_t vband_split;
    {
      unsigned off = s.vband_offsets.arrayZ[b];
      unsigned n = s.vband_curve_counts.arrayZ[b];
      unsigned best_worst = n;
      double best_split = (min_y + max_y) * 0.5;
      unsigned left_count = n;
      for (unsigned ci = 0; ci < n; ci++) {
 double split = s.curve_infos.arrayZ[s.vband_curves.arrayZ[off + ci]].max_y;
 unsigned right_count = ci + 1;
 while (left_count &&
        s.curve_infos.arrayZ[s.vband_curves_asc.arrayZ[off + left_count - 1]].min_y > split)
   left_count--;
 unsigned worst = hb_max (right_count, left_count);
 if (worst < best_worst) {
   best_worst = worst;
   best_split = split;
 }
      }
      vband_split = quantize (best_split);
    }

    unsigned hdr = header_len + num_hbands + b;
    unsigned desc_off = index_offset;

    for (unsigned ci = 0; ci < s.vband_curve_counts.arrayZ[b]; ci++) {
      buf[index_offset].r = encode_offset (s.curve_texel_offset.arrayZ[s.vband_curves.arrayZ[s.vband_offsets.arrayZ[b] + ci]]);
      buf[index_offset].g = 0;
      buf[index_offset].b = 0;
      buf[index_offset].a = 0;
      index_offset++;
    }

    unsigned asc_off = index_offset;

    for (unsigned ci = 0; ci < s.vband_curve_counts.arrayZ[b]; ci++) {
      buf[index_offset].r = encode_offset (s.curve_texel_offset.arrayZ[s.vband_curves_asc.arrayZ[s.vband_offsets.arrayZ[b] + ci]]);
      buf[index_offset].g = 0;
      buf[index_offset].b = 0;
      buf[index_offset].a = 0;
      index_offset++;
    }

    buf[hdr].r = (int16_t) s.vband_curve_counts.arrayZ[b];
    buf[hdr].g = encode_offset (desc_off);
    buf[hdr].b = encode_offset (asc_off);
    buf[hdr].a = vband_split;
  }

  hb_blob_t *recycled = draw->recycled_blob;
  draw->recycled_blob = nullptr;
  return hb_blob_t::recycle_finalize ((char *) buf, buf_capacity, needed_bytes,
    recycled, replaced_recycled_buf);
}


/* ---- Public API ---- */

/**
 * hb_gpu_draw_create_or_fail:
 *
 * Creates a new GPU shape encoder.
 *
 * Return value: (transfer full):
 * A newly allocated #hb_gpu_draw_t, or `NULL` on allocation failure.
 *
 * Since: 14.0.0
 **/

hb_gpu_draw_t *
hb_gpu_draw_create_or_fail (void)
{
  return hb_object_create<hb_gpu_draw_t> ();
}

/**
 * hb_gpu_draw_reference: (skip)
 * @draw: a GPU shape encoder
 *
 * Increases the reference count on @draw by one.
 *
 * Return value: (transfer full):
 * The referenced #hb_gpu_draw_t.
 *
 * Since: 14.0.0
 **/

hb_gpu_draw_t *
hb_gpu_draw_reference (hb_gpu_draw_t *draw)
{
  return hb_object_reference (draw);
}

/**
 * hb_gpu_draw_destroy: (skip)
 * @draw: a GPU shape encoder
 *
 * Decreases the reference count on @draw by one. When the
 * reference count reaches zero, the encoder is freed.
 *
 * Since: 14.0.0
 **/

void
hb_gpu_draw_destroy (hb_gpu_draw_t *draw)
{
  if (!hb_object_should_destroy (draw))
    return;

  hb_blob_destroy (draw->recycled_blob);

  hb_object_actually_destroy (draw);
  hb_free (draw);
}

/**
 * hb_gpu_draw_set_user_data: (skip)
 * @draw: a GPU shape encoder
 * @key: the user-data key
 * @data: a pointer to the user data
 * @destroy: (nullable): a callback to call when @data is not needed anymore
 * @replace: whether to replace an existing data with the same key
 *
 * Attaches user data to @draw.
 *
 * Return value: `true` if success, `false` otherwise
 *
 * Since: 14.0.0
 **/

hb_bool_t
hb_gpu_draw_set_user_data (hb_gpu_draw_t     *draw,
        hb_user_data_key_t *key,
        void               *data,
        hb_destroy_func_t   destroy,
        hb_bool_t           replace)
{
  return hb_object_set_user_data (draw, key, data, destroy, replace);
}

/**
 * hb_gpu_draw_get_user_data: (skip)
 * @draw: a GPU shape encoder
 * @key: the user-data key
 *
 * Fetches the user-data associated with the specified key.
 *
 * Return value: (transfer none):
 * A pointer to the user data
 *
 * Since: 14.0.0
 **/

void *
hb_gpu_draw_get_user_data (const hb_gpu_draw_t     *draw,
        hb_user_data_key_t *key)
{
  return hb_object_get_user_data (draw, key);
}

/**
 * hb_gpu_draw_get_funcs:
 * @draw: a GPU draw context.
 *
 * Fetches the #hb_draw_funcs_t that feeds outline data into
 * @draw.  Pass @draw as the @draw_data argument when calling
 * the draw functions.
 *
 * Return value: (transfer none):
 * The GPU draw functions
 *
 * Since: 14.2.0
 **/

hb_draw_funcs_t *
hb_gpu_draw_get_funcs (const hb_gpu_draw_t *draw HB_UNUSED)
{
  return static_gpu_draw_funcs.get_unconst ();
}

/**
 * hb_gpu_draw_set_scale:
 * @draw: a GPU shape encoder
 * @x_scale: horizontal scale (typically from hb_font_get_scale())
 * @y_scale: vertical scale
 *
 * Sets the font scale so the encoded blob can embed it for
 * shader use (e.g. computing pixels-per-em).  Called
 * automatically by hb_gpu_draw_glyph().
 *
 * Since: 14.1.0
 **/

void
hb_gpu_draw_set_scale (hb_gpu_draw_t *draw,
         int            x_scale,
         int            y_scale)
{
  draw->x_scale = x_scale;
  draw->y_scale = y_scale;
}

/**
 * hb_gpu_draw_get_scale:
 * @draw: a GPU shape encoder
 * @x_scale: (out): horizontal scale
 * @y_scale: (out): vertical scale
 *
 * Gets the font scale previously set via hb_gpu_draw_set_scale() or
 * hb_gpu_draw_glyph().
 *
 * Since: 14.2.0
 **/

void
hb_gpu_draw_get_scale (const hb_gpu_draw_t *draw,
         int                 *x_scale,
         int                 *y_scale)
{
  if (x_scale) *x_scale = draw->x_scale;
  if (y_scale) *y_scale = draw->y_scale;
}

/**
 * hb_gpu_draw_glyph_or_fail:
 * @draw: a GPU shape encoder
 * @font: font to draw from
 * @glyph: glyph ID to draw
 *
 * Convenience to draw one glyph outline.  Equivalent to:
 *
 * |[<!-- language="plain" -->
 * hb_gpu_draw_set_scale (draw, x_scale, y_scale);
 * hb_font_draw_glyph_or_fail (font, glyph,
 *   hb_gpu_draw_get_funcs (draw), draw);
 * ]|
 *
 * Return value: `true` if the glyph was drawn, `false` if the font
 * has no outlines for @glyph.
 *
 * Since: 14.2.0
 **/

hb_bool_t
hb_gpu_draw_glyph_or_fail (hb_gpu_draw_t  *draw,
      hb_font_t      *font,
      hb_codepoint_t  glyph)
{
  int x_scale, y_scale;
  hb_font_get_scale (font, &x_scale, &y_scale);
  hb_gpu_draw_set_scale (draw, x_scale, y_scale);

  return hb_font_draw_glyph_or_fail (font, glyph,
         hb_gpu_draw_get_funcs (draw),
         draw);
}

/**
 * hb_gpu_draw_glyph:
 * @draw: a GPU shape encoder
 * @font: font to draw from
 * @glyph: glyph ID to draw
 *
 * Draws a single glyph outline into the encoder.  Equivalent to
 * hb_gpu_draw_glyph_or_fail() with the return value ignored.
 *
 * Since: 14.0.0
 **/

void
hb_gpu_draw_glyph (hb_gpu_draw_t  *draw,
     hb_font_t      *font,
     hb_codepoint_t  glyph)
{
  hb_gpu_draw_glyph_or_fail (draw, font, glyph);
}

/**
 * hb_gpu_draw_clear:
 * @draw: a GPU shape encoder
 *
 * Discards accumulated outlines so @draw can be reused for another
 * encode.  User configuration (font scale) is preserved.  Call
 * hb_gpu_draw_reset() to also reset user configuration to defaults.
 *
 * Since: 14.2.0
 **/

void
hb_gpu_draw_clear (hb_gpu_draw_t *draw)
{
  draw->start_x = draw->start_y = 0;
  draw->current_x = draw->current_y = 0;
  draw->need_moveto = true;
  draw->num_curves = 0;
  draw->success = true;
  draw->curves.reset ();

  draw->ext_min_x =  HUGE_VAL;
  draw->ext_min_y =  HUGE_VAL;
  draw->ext_max_x = -HUGE_VAL;
  draw->ext_max_y = -HUGE_VAL;
}

/**
 * hb_gpu_draw_reset:
 * @draw: a GPU shape encoder
 *
 * Resets the encoder, discarding all accumulated outlines and
 * resetting user configuration to defaults.
 *
 * Since: 14.0.0
 **/

void
hb_gpu_draw_reset (hb_gpu_draw_t *draw)
{
  draw->x_scale = 0;
  draw->y_scale = 0;
  hb_gpu_draw_clear (draw);
}

/**
 * hb_gpu_draw_recycle_blob:
 * @draw: a GPU shape encoder
 * @blob: (transfer full): a blob previously returned by hb_gpu_draw_encode()
 *
 * Returns a blob to the encoder for potential reuse.
 * The caller transfers ownership of @blob.
 *
 * Currently this simply destroys the blob.  A future version
 * may reclaim the underlying buffer to avoid allocation on the
 * next hb_gpu_draw_encode() call.
 *
 * Since: 14.0.0
 **/

void
hb_gpu_draw_recycle_blob (hb_gpu_draw_t *draw,
       hb_blob_t      *blob)
{
  hb_blob_t::recycle_stash (&draw->recycled_blob, blob);
}


#include "hb-gpu-draw-fragment-glsl.hh"
#include "hb-gpu-draw-fragment-msl.hh"
#include "hb-gpu-draw-fragment-wgsl.hh"
#include "hb-gpu-draw-fragment-hlsl.hh"

/**
 * hb_gpu_draw_shader_source:
 * @stage: pipeline stage (vertex or fragment)
 * @lang: shader language variant
 *
 * Returns the draw-renderer-specific shader source for the
 * specified stage and language.  The returned string is static
 * and must not be freed.
 *
 * This source assumes the shared helpers returned by
 * hb_gpu_shader_source() are concatenated ahead of it.  The
 * caller should assemble the full shader as
 * `#version`-directive + hb_gpu_shader_source() +
 * hb_gpu_draw_shader_source() + caller's `main()`.
 *
 * The vertex stage currently has no draw-specific helpers; this
 * function returns an empty string for that stage so the caller
 * can concatenate unconditionally.
 *
 * Return value: (transfer none):
 * A shader source string, or `NULL` if @stage or @lang is
 * unsupported.
 *
 * Since: 14.2.0
 **/

const char *
hb_gpu_draw_shader_source (hb_gpu_shader_stage_t stage,
      hb_gpu_shader_lang_t  lang)
{
  switch (stage) {
  case HB_GPU_SHADER_STAGE_FRAGMENT:
    switch (lang) {
    case HB_GPU_SHADER_LANG_GLSL: return hb_gpu_draw_fragment_glsl;
    case HB_GPU_SHADER_LANG_MSL:  return hb_gpu_draw_fragment_msl;
    case HB_GPU_SHADER_LANG_WGSL: return hb_gpu_draw_fragment_wgsl;
    case HB_GPU_SHADER_LANG_HLSL: return hb_gpu_draw_fragment_hlsl;
    case HB_GPU_SHADER_LANG_INVALID:
    default: return nullptr;
    }
  case HB_GPU_SHADER_STAGE_VERTEX:
    switch (lang) {
    case HB_GPU_SHADER_LANG_GLSL:
    case HB_GPU_SHADER_LANG_MSL:
    case HB_GPU_SHADER_LANG_WGSL:
    case HB_GPU_SHADER_LANG_HLSL: return "";
    case HB_GPU_SHADER_LANG_INVALID:
    default: return nullptr;
    }
  default:
    return nullptr;
  }
}

Messung V0.5 in Prozent
C=89 H=94 G=91

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet am  2026-09-27) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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.