Example #1
0
uint32_t get_ft2_text_width(wchar_t *text, struct ft2_source *srcdata)
{
	FT_GlyphSlot slot = 0;
	FT_UInt glyph_index = 0;
	uint32_t w = 0, max_w = 0;
	size_t len;

	if (!text)
		return 0;

	len = wcslen(text);
	for (size_t i = 0; i < len; i++) {
		//遇到换行,重置当前行宽度
		if (text[i] == L'\n')
		{
			w = 0;
			continue;
		}

		FT_Face curFace;
		glyph_index = FT_Get_Char_Index_Fallback(srcdata, &curFace, text[i]);
		if (curFace == 0)
			continue;

		slot = curFace->glyph;

		FT_Load_Glyph(curFace, glyph_index, FT_LOAD_DEFAULT);
		if (slot->format == FT_GLYPH_FORMAT_OUTLINE && srcdata->fake_bold)
			FT_Outline_EmboldenXY(&slot->outline, 0x80, 0x40);

		w += slot->advance.x >> 6;
		if (w > max_w) 
			max_w = w;
	}

	FT_Vector size;
	size.x = max_w;
	size.y = srcdata->max_h;

	FT_Vector tmp = size;
	FT_Vector_Transform(&size, &srcdata->transform_matrix);
	FT_Vector_Transform(&tmp, &srcdata->fallback_transform);

	max_w = size.x;
	if (max_w < tmp.x)
		max_w = tmp.x;

	if (max_w > texbuf_w)
		return texbuf_w;
	else
		return max_w;
}
Example #2
0
    inline FT_GlyphSlot load_glyph(unsigned int char_code)
    {
        bool embolden   = property_.xstrength != 0 || property_.ystrength != 0;
        bool distortion = embolden || property_.lean != 0;
        FT_Int32 load_flag = property_.load_flag;
        if (distortion)
            load_flag |= FT_LOAD_NO_BITMAP;

        FT_UInt glyph_index = FT_Get_Char_Index(face_, char_code);
        if (0 != FT_Load_Glyph(face_, glyph_index, load_flag))
            return nullptr;

        FT_GlyphSlot glyph = face_->glyph;
        if (embolden && 0 != FT_Outline_EmboldenXY(&glyph->outline,
                                property_.xstrength, property_.ystrength))
            return nullptr;

        return glyph;
    }
Example #3
0
 FT_Outline_Embolden( FT_Outline*  outline,
                      FT_Pos       strength )
 {
   return FT_Outline_EmboldenXY( outline, strength, strength );
 }
Example #4
0
  static FT_Error
  af_loader_embolden_glyph_in_slot( AF_Loader        loader,
                                    FT_Face          face,
                                    AF_StyleMetrics  style_metrics )
  {
    FT_Error  error = FT_Err_Ok;

    FT_GlyphSlot           slot    = face->glyph;
    AF_FaceGlobals         globals = loader->globals;
    AF_WritingSystemClass  writing_system_class;

    FT_Size_Metrics*  size_metrics = &face->size->internal->autohint_metrics;

    FT_Pos  stdVW = 0;
    FT_Pos  stdHW = 0;

    FT_Bool  size_changed = size_metrics->x_ppem !=
                              globals->stem_darkening_for_ppem;

    FT_Fixed  em_size  = af_intToFixed( face->units_per_EM );
    FT_Fixed  em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size );

    FT_Matrix  scale_down_matrix = { 0x10000L, 0, 0, 0x10000L };


    /* Skip stem darkening for broken fonts. */
    if ( !face->units_per_EM )
    {
      error = FT_ERR( Corrupted_Font_Header );
      goto Exit;
    }

    /*
     * We depend on the writing system (script analyzers) to supply
     * standard widths for the script of the glyph we are looking at.  If
     * it can't deliver, stem darkening is disabled.
     */
    writing_system_class =
      af_writing_system_classes[style_metrics->style_class->writing_system];

    if ( writing_system_class->style_metrics_getstdw )
      writing_system_class->style_metrics_getstdw( style_metrics,
                                                   &stdHW,
                                                   &stdVW );
    else
    {
      error = FT_ERR( Unimplemented_Feature );
      goto Exit;
    }

    if ( size_changed                                               ||
         ( stdVW > 0 && stdVW != globals->standard_vertical_width ) )
    {
      FT_Fixed  darken_by_font_units_x, darken_x;


      darken_by_font_units_x =
        af_intToFixed( af_loader_compute_darkening( loader,
                                                    face,
                                                    stdVW ) );
      darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x,
                                       size_metrics->x_scale ),
                            em_ratio );

      globals->standard_vertical_width = stdVW;
      globals->stem_darkening_for_ppem = size_metrics->x_ppem;
      globals->darken_x                = af_fixedToInt( darken_x );
    }

    if ( size_changed                                                 ||
         ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) )
    {
      FT_Fixed  darken_by_font_units_y, darken_y;


      darken_by_font_units_y =
        af_intToFixed( af_loader_compute_darkening( loader,
                                                    face,
                                                    stdHW ) );
      darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y,
                                       size_metrics->y_scale ),
                            em_ratio );

      globals->standard_horizontal_width = stdHW;
      globals->stem_darkening_for_ppem   = size_metrics->x_ppem;
      globals->darken_y                  = af_fixedToInt( darken_y );

      /*
       * Scale outlines down on the Y-axis to keep them inside their blue
       * zones.  The stronger the emboldening, the stronger the downscaling
       * (plus heuristical padding to prevent outlines still falling out
       * their zones due to rounding).
       *
       * Reason: `FT_Outline_Embolden' works by shifting the rightmost
       * points of stems farther to the right, and topmost points farther
       * up.  This positions points on the Y-axis outside their
       * pre-computed blue zones and leads to distortion when applying the
       * hints in the code further below.  Code outside this emboldening
       * block doesn't know we are presenting it with modified outlines the
       * analyzer didn't see!
       *
       * An unfortunate side effect of downscaling is that the emboldening
       * effect is slightly decreased.  The loss becomes more pronounced
       * versus the CFF driver at smaller sizes, e.g., at 9ppem and below.
       */
      globals->scale_down_factor =
        FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ),
                   em_size );
    }

    FT_Outline_EmboldenXY( &slot->outline,
                           globals->darken_x,
                           globals->darken_y );

    scale_down_matrix.yy = globals->scale_down_factor;
    FT_Outline_Transform( &slot->outline, &scale_down_matrix );

  Exit:
    return error;
  }
Example #5
0
  static FT_Error
  af_loader_load_g( AF_Loader  loader,
                    AF_Scaler  scaler,
                    FT_UInt    glyph_index,
                    FT_Int32   load_flags )
  {
    AF_Module  module = loader->globals->module;

    FT_Error          error;
    FT_Face           face     = loader->face;
    AF_StyleMetrics   metrics  = loader->metrics;
    AF_GlyphHints     hints    = loader->hints;
    FT_GlyphSlot      slot     = face->glyph;
    FT_Slot_Internal  internal = slot->internal;
    FT_GlyphLoader    gloader  = internal->loader;
    FT_Int32          flags;


    flags = load_flags | FT_LOAD_LINEAR_DESIGN;
    error = FT_Load_Glyph( face, glyph_index, flags );
    if ( error )
      goto Exit;

    /*
     * Apply stem darkening (emboldening) here before hints are applied to
     * the outline.  Glyphs are scaled down proportionally to the
     * emboldening so that curve points don't fall outside their precomputed
     * blue zones.
     *
     * Any emboldening done by the font driver (e.g., the CFF driver)
     * doesn't reach here because the autohinter loads the unprocessed
     * glyphs in font units for analysis (functions `af_*_metrics_init_*')
     * and then above to prepare it for the rasterizers by itself,
     * independently of the font driver.  So emboldening must be done here,
     * within the autohinter.
     *
     * All glyphs to be autohinted pass through here one by one.  The
     * standard widths can therefore change from one glyph to the next,
     * depending on what script a glyph is assigned to (each script has its
     * own set of standard widths and other metrics).  The darkening amount
     * must therefore be recomputed for each size and
     * `standard_{vertical,horizontal}_width' change.
     */
    if ( !module->no_stem_darkening )
    {
      AF_FaceGlobals         globals = loader->globals;
      AF_WritingSystemClass  writing_system_class;

      FT_Pos  stdVW = 0;
      FT_Pos  stdHW = 0;

      FT_Bool  size_changed = face->size->metrics.x_ppem
                                != globals->stem_darkening_for_ppem;

      FT_Fixed  em_size  = af_intToFixed( face->units_per_EM );
      FT_Fixed  em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size );

      FT_Matrix  scale_down_matrix = { 0x10000L, 0, 0, 0x10000L };


      /* Skip stem darkening for broken fonts. */
      if ( !face->units_per_EM )
        goto After_Emboldening;

      /*
       * We depend on the writing system (script analyzers) to supply
       * standard widths for the script of the glyph we are looking at.  If
       * it can't deliver, stem darkening is effectively disabled.
       */
      writing_system_class =
        AF_WRITING_SYSTEM_CLASSES_GET[metrics->style_class->writing_system];

      if ( writing_system_class->style_metrics_getstdw )
        writing_system_class->style_metrics_getstdw( metrics,
                                                     &stdHW,
                                                     &stdVW );
      else
        goto After_Emboldening;


      if ( size_changed                                               ||
           ( stdVW > 0 && stdVW != globals->standard_vertical_width ) )
      {
        FT_Fixed  darken_by_font_units_x, darken_x;


        darken_by_font_units_x =
          af_intToFixed( af_loader_compute_darkening( loader,
                                                      face,
                                                      stdVW ) );
        darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x,
                                         face->size->metrics.x_scale ),
                              em_ratio );

        globals->standard_vertical_width = stdVW;
        globals->stem_darkening_for_ppem = face->size->metrics.x_ppem;
        globals->darken_x                = af_fixedToInt( darken_x );
      }

      if ( size_changed                                                 ||
           ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) )
      {
        FT_Fixed  darken_by_font_units_y, darken_y;


        darken_by_font_units_y =
          af_intToFixed( af_loader_compute_darkening( loader,
                                                      face,
                                                      stdHW ) );
        darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y,
                                         face->size->metrics.y_scale ),
                              em_ratio );

        globals->standard_horizontal_width = stdHW;
        globals->stem_darkening_for_ppem   = face->size->metrics.x_ppem;
        globals->darken_y                  = af_fixedToInt( darken_y );

        /*
         * Scale outlines down on the Y-axis to keep them inside their blue
         * zones.  The stronger the emboldening, the stronger the
         * downscaling (plus heuristical padding to prevent outlines still
         * falling out their zones due to rounding).
         *
         * Reason: `FT_Outline_Embolden' works by shifting the rightmost
         * points of stems farther to the right, and topmost points farther
         * up.  This positions points on the Y-axis outside their
         * pre-computed blue zones and leads to distortion when applying the
         * hints in the code further below.  Code outside this emboldening
         * block doesn't know we are presenting it with modified outlines
         * the analyzer didn't see!
         *
         * An unfortunate side effect of downscaling is that the emboldening
         * effect is slightly decreased.  The loss becomes more pronounced
         * versus the CFF driver at smaller sizes, e.g., at 9ppem and below.
         */
        globals->scale_down_factor =
          FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ),
                     em_size );
      }

      FT_Outline_EmboldenXY( &slot->outline,
                             globals->darken_x,
                             globals->darken_y );

      scale_down_matrix.yy = globals->scale_down_factor;
      FT_Outline_Transform( &slot->outline, &scale_down_matrix );
    }

  After_Emboldening:
    loader->transformed = internal->glyph_transformed;
    if ( loader->transformed )
    {
      FT_Matrix  inverse;


      loader->trans_matrix = internal->glyph_matrix;
      loader->trans_delta  = internal->glyph_delta;

      inverse = loader->trans_matrix;
      if ( !FT_Matrix_Invert( &inverse ) )
        FT_Vector_Transform( &loader->trans_delta, &inverse );
    }

    switch ( slot->format )
    {
    case FT_GLYPH_FORMAT_OUTLINE:
      /* translate the loaded glyph when an internal transform is needed */
      if ( loader->transformed )
        FT_Outline_Translate( &slot->outline,
                              loader->trans_delta.x,
                              loader->trans_delta.y );

      /* compute original horizontal phantom points (and ignore */
      /* vertical ones)                                         */
      loader->pp1.x = hints->x_delta;
      loader->pp1.y = hints->y_delta;
      loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance,
                                 hints->x_scale ) + hints->x_delta;
      loader->pp2.y = hints->y_delta;

      /* be sure to check for spacing glyphs */
      if ( slot->outline.n_points == 0 )
        goto Hint_Metrics;

      /* now load the slot image into the auto-outline and run the */
      /* automatic hinting process                                 */
      {
#ifdef FT_CONFIG_OPTION_PIC
        AF_FaceGlobals         globals = loader->globals;
#endif
        AF_StyleClass          style_class = metrics->style_class;
        AF_WritingSystemClass  writing_system_class =
          AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system];


        if ( writing_system_class->style_hints_apply )
          writing_system_class->style_hints_apply( glyph_index,
                                                   hints,
                                                   &gloader->base.outline,
                                                   metrics );
      }

      /* we now need to adjust the metrics according to the change in */
      /* width/positioning that occurred during the hinting process   */
      if ( scaler->render_mode != FT_RENDER_MODE_LIGHT )
      {
        FT_Pos        old_rsb, old_lsb, new_lsb;
        FT_Pos        pp1x_uh, pp2x_uh;
        AF_AxisHints  axis  = &hints->axis[AF_DIMENSION_HORZ];
        AF_Edge       edge1 = axis->edges;         /* leftmost edge  */
        AF_Edge       edge2 = edge1 +
                              axis->num_edges - 1; /* rightmost edge */


        if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) )
        {
          old_rsb = loader->pp2.x - edge2->opos;
          old_lsb = edge1->opos;
          new_lsb = edge1->pos;

          /* remember unhinted values to later account */
          /* for rounding errors                       */

          pp1x_uh = new_lsb    - old_lsb;
          pp2x_uh = edge2->pos + old_rsb;

          /* prefer too much space over too little space */
          /* for very small sizes                        */

          if ( old_lsb < 24 )
            pp1x_uh -= 8;

          if ( old_rsb < 24 )
            pp2x_uh += 8;

          loader->pp1.x = FT_PIX_ROUND( pp1x_uh );
          loader->pp2.x = FT_PIX_ROUND( pp2x_uh );

          if ( loader->pp1.x >= new_lsb && old_lsb > 0 )
            loader->pp1.x -= 64;

          if ( loader->pp2.x <= edge2->pos && old_rsb > 0 )
            loader->pp2.x += 64;

          slot->lsb_delta = loader->pp1.x - pp1x_uh;
          slot->rsb_delta = loader->pp2.x - pp2x_uh;
        }
        else
        {
          FT_Pos  pp1x = loader->pp1.x;
          FT_Pos  pp2x = loader->pp2.x;


          loader->pp1.x = FT_PIX_ROUND( pp1x );
          loader->pp2.x = FT_PIX_ROUND( pp2x );

          slot->lsb_delta = loader->pp1.x - pp1x;
          slot->rsb_delta = loader->pp2.x - pp2x;
        }
      }
      else
      {
        FT_Pos  pp1x = loader->pp1.x;
        FT_Pos  pp2x = loader->pp2.x;


        loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
        loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );

        slot->lsb_delta = loader->pp1.x - pp1x;
        slot->rsb_delta = loader->pp2.x - pp2x;
      }

      break;

    default:
      /* we don't support other formats (yet?) */
      error = FT_THROW( Unimplemented_Feature );
    }

  Hint_Metrics:
    {
      FT_BBox    bbox;
      FT_Vector  vvector;


      vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
      vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
      vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale );
      vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale );

      /* transform the hinted outline if needed */
      if ( loader->transformed )
      {
        FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix );
        FT_Vector_Transform( &vvector, &loader->trans_matrix );
      }
#if 1
      /* we must translate our final outline by -pp1.x and compute */
      /* the new metrics                                           */
      if ( loader->pp1.x )
        FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 );
#endif
      FT_Outline_Get_CBox( &gloader->base.outline, &bbox );

      bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
      bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
      bbox.xMax = FT_PIX_CEIL(  bbox.xMax );
      bbox.yMax = FT_PIX_CEIL(  bbox.yMax );

      slot->metrics.width        = bbox.xMax - bbox.xMin;
      slot->metrics.height       = bbox.yMax - bbox.yMin;
      slot->metrics.horiBearingX = bbox.xMin;
      slot->metrics.horiBearingY = bbox.yMax;

      slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x );
      slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y );

      /* for mono-width fonts (like Andale, Courier, etc.) we need */
      /* to keep the original rounded advance width; ditto for     */
      /* digits if all have the same advance width                 */
#if 0
      if ( !FT_IS_FIXED_WIDTH( slot->face ) )
        slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
      else
        slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
                                               x_scale );
#else
      if ( scaler->render_mode != FT_RENDER_MODE_LIGHT                      &&
           ( FT_IS_FIXED_WIDTH( slot->face )                              ||
             ( af_face_globals_is_digit( loader->globals, glyph_index ) &&
               metrics->digits_have_same_width                          ) ) )
      {
        slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
                                               metrics->scaler.x_scale );

        /* Set delta values to 0.  Otherwise code that uses them is */
        /* going to ruin the fixed advance width.                   */
        slot->lsb_delta = 0;
        slot->rsb_delta = 0;
      }
      else
      {
        /* non-spacing glyphs must stay as-is */
        if ( slot->metrics.horiAdvance )
          slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
      }
#endif

      slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
                                             metrics->scaler.y_scale );

      slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
      slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );

#if 0
      /* reassign all outline fields except flags to protect them */
      slot->outline.n_contours = internal->loader->base.outline.n_contours;
      slot->outline.n_points   = internal->loader->base.outline.n_points;
      slot->outline.points     = internal->loader->base.outline.points;
      slot->outline.tags       = internal->loader->base.outline.tags;
      slot->outline.contours   = internal->loader->base.outline.contours;
#endif

      slot->format  = FT_GLYPH_FORMAT_OUTLINE;
    }

  Exit:
    return error;
  }
Example #6
0
  static FT_Error
  Render_Embolden( int  num_indices,
                   int  first_index )
  {
    int           start_x, start_y, step_y, x, y;
    int           i;
    FT_Size       size;
    FT_Face       face;
    FT_GlyphSlot  slot;

    FT_Pos  xstr, ystr;


    error = FTDemo_Get_Size( handle, &size );

    if ( error )
    {
      /* probably a non-existent bitmap font size */
      return error;
    }

    INIT_SIZE( size, start_x, start_y, step_y, x, y );
    face = size->face;
    slot = face->glyph;

    ystr = status.ptsize * status.res / 72;
    xstr = status.xbold_factor * ystr;
    ystr = status.ybold_factor * ystr;

    for ( i = first_index; i < num_indices; i++ )
    {
      int  gindex;


      if ( handle->encoding == FT_ENCODING_NONE )
        gindex = i;
      else
        gindex = FTDemo_Get_Index( handle, i );

      error = FT_Load_Glyph( face, gindex, handle->load_flags );
      if ( !error )
      {
        /* this is essentially the code of function */
        /* `FT_GlyphSlot_Embolden'                  */

        if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
        {
          error = FT_Outline_EmboldenXY( &slot->outline, xstr, ystr );
          /* ignore error */
        }
        else if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
        {
          /* round to full pixels */
          xstr &= ~63;
          ystr &= ~63;

          error = FT_GlyphSlot_Own_Bitmap( slot );
          if ( error )
            goto Next;

          error = FT_Bitmap_Embolden( slot->library, &slot->bitmap,
                                      xstr, ystr );
          if ( error )
            goto Next;
        } else
          goto Next;

        if ( slot->advance.x )
          slot->advance.x += xstr;

        if ( slot->advance.y )
          slot->advance.y += ystr;

        slot->metrics.width        += xstr;
        slot->metrics.height       += ystr;
        slot->metrics.horiAdvance  += xstr;
        slot->metrics.vertAdvance  += ystr;

        if ( slot->format == FT_GLYPH_FORMAT_BITMAP )
          slot->bitmap_top += ystr >> 6;

        error = FTDemo_Draw_Slot( handle, display, slot, &x, &y );

        if ( error )
          goto Next;
        else if ( X_TOO_LONG( x, size, display ) )
        {
          x  = start_x;
          y += step_y;

          if ( Y_TOO_LONG( y, size, display ) )
            break;
        }
      }
      else
    Next:
        status.Fail++;
    }
Example #7
0
void cache_glyphs(struct ft2_source *srcdata, wchar_t *cache_glyphs)
{
	FT_GlyphSlot slot = 0;
	FT_UInt glyph_index = 0;

	if (!srcdata->font_face || !cache_glyphs)
		return;

	//slot = srcdata->font_face->glyph;

	uint32_t dx = srcdata->texbuf_x, dy = srcdata->texbuf_y;
	uint8_t alpha;

	int32_t cached_glyphs = 0;
	size_t len = wcslen(cache_glyphs);

	for (size_t i = 0; i < len; i++) {
		FT_Face curFace;
		glyph_index = FT_Get_Char_Index_Fallback(srcdata, &curFace,
			cache_glyphs[i]);

		if (curFace == 0)
			goto skip_glyph;

		slot = curFace->glyph;

		if (src_glyph != NULL)
			goto skip_glyph;

		FT_Load_Glyph(curFace, glyph_index, FT_LOAD_DEFAULT);

		bool isUseFallback = slot->face == srcdata->fallback_face;
		bool shouldBolden = ((!isUseFallback && srcdata->fake_bold) || (isUseFallback && srcdata->fallback_fake_bold));

		if (slot->format == FT_GLYPH_FORMAT_OUTLINE && shouldBolden)
			FT_Outline_EmboldenXY(&slot->outline, 0x80, 0x40);

		FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);

		uint32_t g_w = slot->bitmap.width;
		uint32_t g_h = slot->bitmap.rows;

		if (srcdata->max_h < g_h) srcdata->max_h = g_h;

		if (dx + g_w >= texbuf_w) {
			dx = 0;
			dy += srcdata->max_h + 1;
		}

		src_glyph = bzalloc(sizeof(struct glyph_info));
		src_glyph->u = (float)dx / (float)texbuf_w;
		src_glyph->u2 = (float)(dx + g_w) / (float)texbuf_w;
		src_glyph->v = (float)dy / (float)texbuf_h;
		src_glyph->v2 = (float)(dy + g_h) / (float)texbuf_h;
		src_glyph->w = g_w;
		src_glyph->h = g_h;
		src_glyph->yoff = slot->bitmap_top;
		src_glyph->xoff = slot->bitmap_left;
		src_glyph->xadv = slot->advance.x >> 6;

		for (uint32_t y = 0; y < g_h; y++) {
			for (uint32_t x = 0; x < g_w; x++) {
				alpha = slot->bitmap.buffer[glyph_pos];
				srcdata->texbuf[buf_pos] =
					0x00FFFFFF ^ ((uint32_t)alpha << 24);
			}
		}

		dx += (g_w + 1);
		if (dx >= texbuf_w) {
			dx = 0;
			dy += srcdata->max_h;
		}

		cached_glyphs++;
	skip_glyph:;
	}

	srcdata->texbuf_x = dx;
	srcdata->texbuf_y = dy;

	if (cached_glyphs > 0) {

		obs_enter_graphics();

		if (srcdata->tex != NULL) {
			gs_texture_t *tmp_texture = NULL;
			tmp_texture = srcdata->tex;
			srcdata->tex = NULL;
			gs_texture_destroy(tmp_texture);
		}

		srcdata->tex = gs_texture_create(texbuf_w, texbuf_h,
			GS_RGBA, 1, (const uint8_t **)&srcdata->texbuf, 0);

		obs_leave_graphics();
	}
}