diff options
author | Campbell Barton <ideasman42@gmail.com> | 2021-10-06 02:44:11 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2021-10-06 02:55:34 +0300 |
commit | fd592538d983bec51e331f1d073c39582d43520f (patch) | |
tree | 9e767692b403fd25ba559be64030f832f5fb3ce6 /source/blender/blenkernel | |
parent | 26dac33ce18f8a5655883b759d271da4a9b94982 (diff) |
Cleanup: move BLI_vfontdata.h to BKE_vfontdata.h
This didn't belong on blenlib since it uses DNA data types
and included a bad-level call to BKE_curve.h.
It also meant linking in blenlib would depend on the freetype library,
noticeable for thumbnail extraction (see D6408).
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_vfontdata.h | 60 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 8 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/font.c | 12 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/vfontdata_freetype.c | 558 |
4 files changed, 632 insertions, 6 deletions
diff --git a/source/blender/blenkernel/BKE_vfontdata.h b/source/blender/blenkernel/BKE_vfontdata.h new file mode 100644 index 00000000000..b162958f69d --- /dev/null +++ b/source/blender/blenkernel/BKE_vfontdata.h @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +#pragma once + +/** \file + * \ingroup bke + * \brief A structure to represent vector fonts, + * and to load them from PostScript fonts. + */ + +#include "DNA_listBase.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct PackedFile; +struct VFont; + +typedef struct VFontData { + struct GHash *characters; + char name[128]; + float scale; + /* Calculated from the font. */ + float em_height; + float ascender; +} VFontData; + +typedef struct VChar { + ListBase nurbsbase; + unsigned int index; + float width; +} VChar; + +VFontData *BKE_vfontdata_from_freetypefont(struct PackedFile *pf); +VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int flag); + +VChar *BKE_vfontdata_char_from_freetypefont(struct VFont *vfont, unsigned long character); +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src, const int flag); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 37581ad5c00..e727730770c 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -60,6 +60,9 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} + + # For `vfontdata_freetype.c`. + ${FREETYPE_INCLUDE_DIRS} ) set(SRC @@ -286,6 +289,7 @@ set(SRC intern/tracking_util.c intern/undo_system.c intern/unit.c + intern/vfontdata_freetype.c intern/volume.cc intern/volume_render.cc intern/volume_to_mesh.cc @@ -453,6 +457,7 @@ set(SRC BKE_tracking.h BKE_undo_system.h BKE_unit.h + BKE_vfontdata.h BKE_volume.h BKE_volume_render.h BKE_volume_to_mesh.hh @@ -502,6 +507,9 @@ set(LIB bf_rna bf_shader_fx bf_simulation + + # For `vfontdata_freetype.c`. + ${FREETYPE_LIBRARY} ) if(WITH_BINRELOC) diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 0e159418724..d749237971a 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -40,7 +40,6 @@ #include "BLI_string_utf8.h" #include "BLI_threads.h" #include "BLI_utildefines.h" -#include "BLI_vfontdata.h" #include "BLT_translation.h" @@ -57,6 +56,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_packedFile.h" +#include "BKE_vfontdata.h" #include "BLO_read_write.h" @@ -77,7 +77,7 @@ static void vfont_init_data(ID *id) if (pf) { VFontData *vfd; - vfd = BLI_vfontdata_from_freetypefont(pf); + vfd = BKE_vfontdata_from_freetypefont(pf); if (vfd) { vfont->data = vfd; @@ -107,7 +107,7 @@ static void vfont_copy_data(Main *UNUSED(bmain), } if (vfont_dst->data) { - vfont_dst->data = BLI_vfontdata_copy(vfont_dst->data, flag_subdata); + vfont_dst->data = BKE_vfontdata_copy(vfont_dst->data, flag_subdata); } } @@ -300,7 +300,7 @@ static VFontData *vfont_get_data(VFont *vfont) } if (pf) { - vfont->data = BLI_vfontdata_from_freetypefont(pf); + vfont->data = BKE_vfontdata_from_freetypefont(pf); if (pf != vfont->packedfile) { BKE_packedfile_free(pf); } @@ -335,7 +335,7 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath) if (pf) { VFontData *vfd; - vfd = BLI_vfontdata_from_freetypefont(pf); + vfd = BKE_vfontdata_from_freetypefont(pf); if (vfd) { /* If there's a font name, use it for the ID name. */ vfont = BKE_libblock_alloc(bmain, ID_VF, vfd->name[0] ? vfd->name : filename, 0); @@ -954,7 +954,7 @@ static bool vfont_to_curve(Object *ob, * happen often once all the chars are load. */ if ((che = find_vfont_char(vfd, ascii)) == NULL) { - che = BLI_vfontchar_from_freetypefont(vfont, ascii); + che = BKE_vfontdata_char_from_freetypefont(vfont, ascii); } BLI_rw_mutex_unlock(&vfont_rwlock); } diff --git a/source/blender/blenkernel/intern/vfontdata_freetype.c b/source/blender/blenkernel/intern/vfontdata_freetype.c new file mode 100644 index 00000000000..bba6768017d --- /dev/null +++ b/source/blender/blenkernel/intern/vfontdata_freetype.c @@ -0,0 +1,558 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is written by Rob Haarsma (phase) + * All rights reserved. + * + * This code parses the Freetype font outline data to chains of Blender's bezier-triples. + * Additional information can be found at the bottom of this file. + * + * Code that uses exotic character maps is present but commented out. + */ + +/** \file + * \ingroup bli + */ + +#include <ft2build.h> +#include FT_FREETYPE_H +/* not needed yet */ +// #include FT_GLYPH_H +// #include FT_BBOX_H +// #include FT_SIZES_H +// #include <freetype/ttnameid.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_utildefines.h" + +#include "BKE_curve.h" +#include "BKE_vfontdata.h" + +#include "DNA_curve_types.h" +#include "DNA_packedFile_types.h" +#include "DNA_vfont_types.h" + +/* local variables */ +static FT_Library library; +static FT_Error err; + +static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData *vfd) +{ + const float scale = vfd->scale; + const float eps = 0.0001f; + const float eps_sq = eps * eps; + /* Blender */ + struct Nurb *nu; + struct VChar *che; + struct BezTriple *bezt; + + /* Freetype2 */ + FT_GlyphSlot glyph; + FT_UInt glyph_index; + FT_Outline ftoutline; + float dx, dy; + int j, k, l, l_first = 0; + + /* + * Generate the character 3D data + * + * Get the FT Glyph index and load the Glyph */ + glyph_index = FT_Get_Char_Index(face, charcode); + err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); + + /* If loading succeeded, convert the FT glyph to the internal format */ + if (!err) { + /* initialize as -1 to add 1 on first loop each time */ + int contour_prev; + int *onpoints; + + /* First we create entry for the new character to the character list */ + che = (VChar *)MEM_callocN(sizeof(struct VChar), "objfnt_char"); + + /* Take some data for modifying purposes */ + glyph = face->glyph; + ftoutline = glyph->outline; + + /* Set the width and character code */ + che->index = charcode; + che->width = glyph->advance.x * scale; + + BLI_ghash_insert(vfd->characters, POINTER_FROM_UINT(che->index), che); + + /* Start converting the FT data */ + onpoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "onpoints"); + + /* get number of on-curve points for beziertriples (including conic virtual on-points) */ + for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { + const int n = ftoutline.contours[j] - contour_prev; + contour_prev = ftoutline.contours[j]; + + for (k = 0; k < n; k++) { + l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k; + if (k == 0) { + l_first = l; + } + + if (ftoutline.tags[l] == FT_Curve_Tag_On) { + onpoints[j]++; + } + + { + const int l_next = (k < n - 1) ? (l + 1) : l_first; + if (ftoutline.tags[l] == FT_Curve_Tag_Conic && + ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { + onpoints[j]++; + } + } + } + } + + /* contour loop, bezier & conic styles merged */ + for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { + const int n = ftoutline.contours[j] - contour_prev; + contour_prev = ftoutline.contours[j]; + + /* add new curve */ + nu = (Nurb *)MEM_callocN(sizeof(struct Nurb), "objfnt_nurb"); + bezt = (BezTriple *)MEM_callocN((onpoints[j]) * sizeof(BezTriple), "objfnt_bezt"); + BLI_addtail(&che->nurbsbase, nu); + + nu->type = CU_BEZIER; + nu->pntsu = onpoints[j]; + nu->resolu = 8; + nu->flagu = CU_NURB_CYCLIC; + nu->bezt = bezt; + + /* individual curve loop, start-end */ + for (k = 0; k < n; k++) { + l = (j > 0) ? (k + ftoutline.contours[j - 1] + 1) : k; + if (k == 0) { + l_first = l; + } + + /* virtual conic on-curve points */ + { + const int l_next = (k < n - 1) ? (l + 1) : l_first; + if (ftoutline.tags[l] == FT_Curve_Tag_Conic && + ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { + dx = (ftoutline.points[l].x + ftoutline.points[l_next].x) * scale / 2.0f; + dy = (ftoutline.points[l].y + ftoutline.points[l_next].y) * scale / 2.0f; + + /* left handle */ + bezt->vec[0][0] = (dx + (2 * ftoutline.points[l].x) * scale) / 3.0f; + bezt->vec[0][1] = (dy + (2 * ftoutline.points[l].y) * scale) / 3.0f; + + /* midpoint (virtual on-curve point) */ + bezt->vec[1][0] = dx; + bezt->vec[1][1] = dy; + + /* right handle */ + bezt->vec[2][0] = (dx + (2 * ftoutline.points[l_next].x) * scale) / 3.0f; + bezt->vec[2][1] = (dy + (2 * ftoutline.points[l_next].y) * scale) / 3.0f; + + bezt->h1 = bezt->h2 = HD_ALIGN; + bezt->radius = 1.0f; + bezt++; + } + } + + /* on-curve points */ + if (ftoutline.tags[l] == FT_Curve_Tag_On) { + const int l_prev = (k > 0) ? (l - 1) : ftoutline.contours[j]; + const int l_next = (k < n - 1) ? (l + 1) : l_first; + + /* left handle */ + if (ftoutline.tags[l_prev] == FT_Curve_Tag_Cubic) { + bezt->vec[0][0] = ftoutline.points[l_prev].x * scale; + bezt->vec[0][1] = ftoutline.points[l_prev].y * scale; + bezt->h1 = HD_FREE; + } + else if (ftoutline.tags[l_prev] == FT_Curve_Tag_Conic) { + bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_prev].x)) * scale / + 3.0f; + bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_prev].y)) * scale / + 3.0f; + bezt->h1 = HD_FREE; + } + else { + bezt->vec[0][0] = ftoutline.points[l].x * scale - + (ftoutline.points[l].x - ftoutline.points[l_prev].x) * scale / 3.0f; + bezt->vec[0][1] = ftoutline.points[l].y * scale - + (ftoutline.points[l].y - ftoutline.points[l_prev].y) * scale / 3.0f; + bezt->h1 = HD_VECT; + } + + /* midpoint (on-curve point) */ + bezt->vec[1][0] = ftoutline.points[l].x * scale; + bezt->vec[1][1] = ftoutline.points[l].y * scale; + + /* right handle */ + if (ftoutline.tags[l_next] == FT_Curve_Tag_Cubic) { + bezt->vec[2][0] = ftoutline.points[l_next].x * scale; + bezt->vec[2][1] = ftoutline.points[l_next].y * scale; + bezt->h2 = HD_FREE; + } + else if (ftoutline.tags[l_next] == FT_Curve_Tag_Conic) { + bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l_next].x)) * scale / + 3.0f; + bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l_next].y)) * scale / + 3.0f; + bezt->h2 = HD_FREE; + } + else { + bezt->vec[2][0] = ftoutline.points[l].x * scale - + (ftoutline.points[l].x - ftoutline.points[l_next].x) * scale / 3.0f; + bezt->vec[2][1] = ftoutline.points[l].y * scale - + (ftoutline.points[l].y - ftoutline.points[l_next].y) * scale / 3.0f; + bezt->h2 = HD_VECT; + } + + /* get the handles that are aligned, tricky... + * - check if one of them is a vector handle. + * - dist_squared_to_line_v2, check if the three beztriple points are on one line + * - len_squared_v2v2, see if there's a distance between the three points + * - len_squared_v2v2 again, to check the angle between the handles + */ + if ((bezt->h1 != HD_VECT && bezt->h2 != HD_VECT) && + (dist_squared_to_line_v2(bezt->vec[0], bezt->vec[1], bezt->vec[2]) < + (0.001f * 0.001f)) && + (len_squared_v2v2(bezt->vec[0], bezt->vec[1]) > eps_sq) && + (len_squared_v2v2(bezt->vec[1], bezt->vec[2]) > eps_sq) && + (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > eps_sq) && + (len_squared_v2v2(bezt->vec[0], bezt->vec[2]) > + max_ff(len_squared_v2v2(bezt->vec[0], bezt->vec[1]), + len_squared_v2v2(bezt->vec[1], bezt->vec[2])))) { + bezt->h1 = bezt->h2 = HD_ALIGN; + } + bezt->radius = 1.0f; + bezt++; + } + } + } + + MEM_freeN(onpoints); + + return che; + } + + return NULL; +} + +static VChar *objchr_to_ftvfontdata(VFont *vfont, FT_ULong charcode) +{ + VChar *che; + + /* Freetype2 */ + FT_Face face; + + /* Load the font to memory */ + if (vfont->temp_pf) { + err = FT_New_Memory_Face(library, vfont->temp_pf->data, vfont->temp_pf->size, 0, &face); + if (err) { + return NULL; + } + } + else { + err = true; + return NULL; + } + + /* Read the char */ + che = freetypechar_to_vchar(face, charcode, vfont->data); + + /* And everything went ok */ + return che; +} + +static VFontData *objfnt_to_ftvfontdata(PackedFile *pf) +{ + /* Variables */ + FT_Face face; + const FT_ULong charcode_reserve = 256; + FT_ULong charcode = 0, lcode; + FT_UInt glyph_index; + VFontData *vfd; + + /* load the freetype font */ + err = FT_New_Memory_Face(library, pf->data, pf->size, 0, &face); + + if (err) { + return NULL; + } + + /* allocate blender font */ + vfd = MEM_callocN(sizeof(*vfd), "FTVFontData"); + + /* Get the name. */ + if (face->family_name) { + BLI_snprintf(vfd->name, sizeof(vfd->name), "%s %s", face->family_name, face->style_name); + BLI_str_utf8_invalid_strip(vfd->name, strlen(vfd->name)); + } + + /* Select a character map. */ + err = FT_Select_Charmap(face, FT_ENCODING_UNICODE); + if (err) { + err = FT_Select_Charmap(face, FT_ENCODING_APPLE_ROMAN); + } + if (err && face->num_charmaps > 0) { + err = FT_Select_Charmap(face, face->charmaps[0]->encoding); + } + if (err) { + FT_Done_Face(face); + MEM_freeN(vfd); + return NULL; + } + + /* Extract the first 256 character from TTF */ + lcode = charcode = FT_Get_First_Char(face, &glyph_index); + + /* Blender default BFont is not "complete". */ + const bool complete_font = (face->ascender != 0) && (face->descender != 0) && + (face->ascender != face->descender); + + if (complete_font) { + /* We can get descender as well, but we simple store descender in relation to the ascender. + * Also note that descender is stored as a negative number. */ + vfd->ascender = (float)face->ascender / (face->ascender - face->descender); + } + else { + vfd->ascender = 0.8f; + vfd->em_height = 1.0f; + } + + /* Adjust font size */ + if (face->bbox.yMax != face->bbox.yMin) { + vfd->scale = (float)(1.0 / (double)(face->bbox.yMax - face->bbox.yMin)); + + if (complete_font) { + vfd->em_height = (float)(face->ascender - face->descender) / + (face->bbox.yMax - face->bbox.yMin); + } + } + else { + vfd->scale = 1.0f / 1000.0f; + } + + /* Load characters */ + vfd->characters = BLI_ghash_int_new_ex(__func__, charcode_reserve); + + while (charcode < charcode_reserve) { + /* Generate the font data */ + freetypechar_to_vchar(face, charcode, vfd); + + /* Next glyph */ + charcode = FT_Get_Next_Char(face, charcode, &glyph_index); + + /* Check that we won't start infinite loop */ + if (charcode <= lcode) { + break; + } + lcode = charcode; + } + + return vfd; +} + +static bool check_freetypefont(PackedFile *pf) +{ + FT_Face face = NULL; + FT_UInt glyph_index = 0; + bool success = false; + + err = FT_New_Memory_Face(library, pf->data, pf->size, 0, &face); + if (err) { + return false; + // XXX error("This is not a valid font"); + } + + FT_Get_First_Char(face, &glyph_index); + if (glyph_index) { + err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); + if (!err) { + success = (face->glyph->format == ft_glyph_format_outline); + } + } + + FT_Done_Face(face); + + return success; +} + +/** + * Construct a new VFontData structure from + * Freetype font data in a PackedFile. + * + * \param pf: The font data. + * \retval A new VFontData structure, or NULL + * if unable to load. + */ +VFontData *BKE_vfontdata_from_freetypefont(PackedFile *pf) +{ + VFontData *vfd = NULL; + + /* init Freetype */ + err = FT_Init_FreeType(&library); + if (err) { + /* XXX error("Failed to load the Freetype font library"); */ + return NULL; + } + + if (check_freetypefont(pf)) { + vfd = objfnt_to_ftvfontdata(pf); + } + + /* free Freetype */ + FT_Done_FreeType(library); + + return vfd; +} + +static void *vfontdata_copy_characters_value_cb(const void *src) +{ + return BKE_vfontdata_char_copy(src, 0); +} + +VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag)) +{ + VFontData *vfont_dst = MEM_dupallocN(vfont_src); + + if (vfont_src->characters != NULL) { + vfont_dst->characters = BLI_ghash_copy( + vfont_src->characters, NULL, vfontdata_copy_characters_value_cb); + } + + return vfont_dst; +} + +VChar *BKE_vfontdata_char_from_freetypefont(VFont *vfont, unsigned long character) +{ + VChar *che = NULL; + + if (!vfont) { + return NULL; + } + + /* Init Freetype */ + err = FT_Init_FreeType(&library); + if (err) { + /* XXX error("Failed to load the Freetype font library"); */ + return NULL; + } + + /* Load the character */ + che = objchr_to_ftvfontdata(vfont, character); + + /* Free Freetype */ + FT_Done_FreeType(library); + + return che; +} + +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src, const int UNUSED(flag)) +{ + VChar *vchar_dst = MEM_dupallocN(vchar_src); + + BLI_listbase_clear(&vchar_dst->nurbsbase); + BKE_nurbList_duplicate(&vchar_dst->nurbsbase, &vchar_src->nurbsbase); + + return vchar_dst; +} + +/** + * from: http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-1 + * + * Vectorial representation of Freetype glyphs + * + * The source format of outlines is a collection of closed paths called "contours". Each contour is + * made of a series of line segments and bezier arcs. Depending on the file format, these can be + * second-order or third-order polynomials. The former are also called quadratic or conic arcs, and + * they come from the TrueType format. The latter are called cubic arcs and mostly come from the + * Type1 format. + * + * Each arc is described through a series of start, end and control points. + * Each point of the outline has a specific tag which indicates whether it is + * used to describe a line segment or an arc. + * The following rules are applied to decompose the contour's points into segments and arcs : + * + * # two successive "on" points indicate a line segment joining them. + * + * # one conic "off" point amidst two "on" points indicates a conic bezier arc, + * the "off" point being the control point, and the "on" ones the start and end points. + * + * # Two successive cubic "off" points amidst two "on" points indicate a cubic bezier arc. + * There must be exactly two cubic control points and two on points for each cubic arc + * (using a single cubic "off" point between two "on" points is forbidden, for example). + * + * # finally, two successive conic "off" points forces the rasterizer to create + * (during the scan-line conversion process exclusively) a virtual "on" point amidst them, + * at their exact middle. + * This greatly facilitates the definition of successive conic bezier arcs. + * Moreover, it's the way outlines are described in the TrueType specification. + * + * Note that it is possible to mix conic and cubic arcs in a single contour, even though no current + * font driver produces such outlines. + * + * <pre> + * * # on + * * off + * __---__ + * #-__ _-- -_ + * --__ _- - + * --__ # \ + * --__ # + * -# + * Two "on" points + * Two "on" points and one "conic" point + * between them + * * + * # __ Two "on" points with two "conic" + * \ - - points between them. The point + * \ / \ marked '0' is the middle of the + * - 0 \ "off" points, and is a 'virtual' + * -_ _- # "on" point where the curve passes. + * -- It does not appear in the point + * list. + * * + * * # on + * * * off + * __---__ + * _-- -_ + * _- - + * # \ + * # + * + * Two "on" points + * and two "cubic" point + * between them + * </pre> + * + * Each glyphs original outline points are located on a grid of indivisible units. + * The points are stored in the font file as 16-bit integer grid coordinates, + * with the grid origin's being at (0, 0); they thus range from -16384 to 16383. + * + * Convert conic to bezier arcs: + * Conic P0 P1 P2 + * Bezier B0 B1 B2 B3 + * B0=P0 + * B1=(P0+2*P1)/3 + * B2=(P2+2*P1)/3 + * B3=P2 + */ |