Commit 7eaf63cb authored by Paul Asmuth's avatar Paul Asmuth
Browse files

move harfbuzz binding into text/text_shaper.{h,cc}

parent 43c065ab
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <string>

#include "colour.h"
#include <signaltk/text/text_shaper.h>
#include <signaltk/core/rasterize.h>

namespace signaltk {
@@ -34,6 +35,7 @@ struct Layer {
  uint32_t width;
  uint32_t height;
  uint32_t dpi;
  text::TextShaper text_shaper;
  Rasterizer rasterizer;
};

+34 −51
Original line number Diff line number Diff line
@@ -7,74 +7,57 @@
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include "text.h"
#include <iostream>
#include <cairo-ft.h>

#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftadvanc.h>
#include <freetype/ftsnames.h>
#include <freetype/tttables.h>

#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>
#include <harfbuzz/hb-icu.h>
#include <signaltk/core/text.h>
#include <signaltk/text/text_shaper.h>
#include <signaltk/core/layer.h>

namespace signaltk {

TextStyle::TextStyle() :
    halign(TextHAlign::LEFT),
    valign(TextVAlign::CENTER),
    font_size(12) {}
    font_size(32) {}

void drawText(
Status drawText(
    const std::string& text,
    const TextStyle& text_style,
    double x,
    double y,
    Layer* layer) {
  auto ctx = layer->ctx;
  auto ctx = layer->rasterizer.ctx;
  auto dpi = layer->dpi;

  auto hb_buf = hb_buffer_create();
  hb_buffer_set_unicode_funcs(hb_buf, hb_icu_get_unicode_funcs());
  hb_buffer_set_direction(hb_buf, HB_DIRECTION_LTR);
  hb_buffer_set_script(hb_buf, HB_SCRIPT_LATIN);

  FT_Library ft_library;
  assert(!FT_Init_FreeType(&ft_library));

  FT_Face ft_face;
  assert(!FT_New_Face(ft_library, "/usr/share/fonts/google-roboto/Roboto-Regular.ttf", 0, &ft_face));
  assert(!FT_Set_Char_Size(ft_face, 0, text_style.font_size * 64, dpi, dpi));

  auto hb_font = hb_ft_font_create(ft_face, NULL);
  auto cairo_face = cairo_ft_font_face_create_for_ft_face(ft_face, 0);

  hb_buffer_add_utf8(hb_buf, text.data(), text.size(), 0, text.size());
  hb_shape(hb_font, hb_buf, NULL, 0);

  unsigned int glyph_count;
  auto hb_glyph_info = hb_buffer_get_glyph_infos(hb_buf, &glyph_count);
  auto hb_glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &glyph_count);
  auto cairo_glyphs = new cairo_glyph_t[glyph_count];

  double g_x = x;
  for (int i = 0; i < glyph_count; ++i) {
    cairo_glyphs[i].index = hb_glyph_info[i].codepoint;
    cairo_glyphs[i].x = g_x + (hb_glyph_pos[i].x_offset / 64.0);
    cairo_glyphs[i].y = y;
    g_x += (hb_glyph_pos[i].x_advance / 64.0);
  FontInfo font_info {
    .font_file = "/Library/Fonts/Arial.ttf",
    .font_size = 42
  };

  std::vector<GlyphPlacement> glyphs;
  auto rc = layer->text_shaper.shapeText(
      text,
      font_info,
      [&glyphs] (const text::TextShaper::GlyphPlacement& gi) {
         GlyphPlacement g;
         glyphs.emplace_back(GlyphPlacement {
           .codepoint = gi.codepoint,
           .x = 100,
           .y = 100
         });
      });

  if (rc != OK) {
    return rc;
  }

  cairo_set_source_rgba(ctx, 0.5, 0.5, 0.5, 1.0);
  cairo_set_font_face(ctx, cairo_face);
  cairo_set_font_size(ctx, text_style.font_size);
  cairo_show_glyphs(ctx, cairo_glyphs, glyph_count);
  return drawTextGlyphs(font_info, glyphs.data(), glyphs.size(), layer);
}

  delete cairo_glyphs;
  hb_buffer_destroy(hb_buf);
Status drawTextGlyphs(
    const FontInfo& font_info,
    const GlyphPlacement* glyphs,
    size_t glyph_count,
    Layer* layer) {
  return layer->rasterizer.drawTextGlyphs(font_info, glyphs, glyph_count);
}

} // namespace signaltk
+20 −2
Original line number Diff line number Diff line
@@ -8,10 +8,11 @@
 * <http://www.gnu.org/licenses/>.
 */
#pragma once
#include "layer.h"
#include <signaltk.h>
#include "path.h"

namespace signaltk {
class Layer;

enum class TextHAlign {
  LEFT, CENTER, RIGHT
@@ -28,13 +29,30 @@ struct TextStyle {
  double font_size;
};

void drawText(
struct FontInfo {
  std::string font_file;
  double font_size;
};

struct GlyphPlacement {
  uint32_t codepoint;
  double x;
  double y;
};

Status drawText(
    const std::string& text,
    const TextStyle& text_style,
    double x,
    double y,
    Layer* layer);

Status drawTextGlyphs(
    const FontInfo& font_info,
    const GlyphPlacement* glyphs,
    size_t glyph_count,
    Layer* layer);


} // namespace signaltk
+78 −0
Original line number Diff line number Diff line
/**
 * This file is part of the "signaltk" project
 *   Copyright (c) 2018 Paul Asmuth
 *
 * libstx is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include <signaltk/text/text_shaper.h>

namespace signaltk {
namespace text {

TextShaper::TextShaper(
    double dpi_) :
    dpi(dpi_),
    ft_ready(false),
    hb_buf(hb_buffer_create()) {}

TextShaper::~TextShaper() {
  if (ft_ready) {
    FT_Done_FreeType(ft);
  }
}

Status TextShaper::shapeText(
    const std::string& text,
    const FontInfo& font_info,
    std::function<void (const TextShaper::GlyphPlacement&)> glyph_cb) {
  if (!ft_ready) {
    if (FT_Init_FreeType(&ft)) {
      return ERROR;
    }

    ft_ready = true;
  }

  // FIXME cache
  FT_Face ft_font;
  if (FT_New_Face(ft, font_info.font_file.c_str(), 0, &ft_font)) {
    return ERROR;
  }

  if (FT_Set_Char_Size(ft_font, 0, font_info.font_size * 64, dpi, dpi)) {
    FT_Done_Face(ft_font);
    return ERROR;
  }

  auto hb_font = hb_ft_font_create_referenced(ft_font);

  hb_buffer_reset(hb_buf);
  hb_buffer_set_direction(hb_buf, HB_DIRECTION_LTR);
  hb_buffer_set_script(hb_buf, HB_SCRIPT_LATIN);

  hb_buffer_add_utf8(hb_buf, text.data(), text.size(), 0, text.size());
  hb_shape(hb_font, hb_buf, NULL, 0);

  uint32_t glyph_count;
  auto glyph_infos = hb_buffer_get_glyph_infos(hb_buf, &glyph_count);
  auto glyph_positions = hb_buffer_get_glyph_positions(hb_buf, &glyph_count);
  for (size_t i = 0; i < glyph_count; ++i) {
    GlyphPlacement  g;
    g.codepoint = glyph_infos[i].codepoint;
    g.advance_x = glyph_positions[i].x_advance / 64.0;
    g.advance_y = glyph_positions[i].y_advance / 64.0;
    glyph_cb(g);
  }

  hb_font_destroy(hb_font);
  FT_Done_Face(ft_font);

  return OK;
}

} // namespace text
} // namespace signaltk
+57 −0
Original line number Diff line number Diff line
/**
 * This file is part of the "signaltk" project
 *   Copyright (c) 2018 Paul Asmuth
 *
 * libstx is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#pragma once
#include <string>
#include <unordered_map>
#include <functional>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <freetype/ftadvanc.h>
#include <freetype/ftsnames.h>
#include <freetype/tttables.h>

#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>
#include <harfbuzz/hb-icu.h>

#include <signaltk/core/text.h>

namespace signaltk {
namespace text {

class TextShaper {
public:

  struct GlyphPlacement {
    uint32_t codepoint;
    double advance_y;
    double advance_x;
  };

  TextShaper(double dpi);
  ~TextShaper();
  TextShaper(const TextShaper&) = delete;
  TextShaper& operator=(const TextShaper&) = delete;

  Status shapeText(
      const std::string& text,
      const FontInfo& font_info,
      std::function<void (const GlyphPlacement&)> glyph_cb);

protected:
  double dpi;
  FT_Library ft;
  bool ft_ready;
  hb_buffer_t* hb_buf;
};

} // namespace text
} // namespace signaltk