From: Paul Wisbey Date: Fri, 27 Nov 2015 15:28:55 +0000 (+0000) Subject: Vector-based text rendering X-Git-Tag: dali_1.1.31~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F84%2F56484%2F21;p=platform%2Fcore%2Fuifw%2Fdali-adaptor.git Vector-based text rendering Change-Id: I1919a3fc7bddf399927d4ffc082af716b9f1032f --- diff --git a/build/tizen/adaptor/Makefile.am b/build/tizen/adaptor/Makefile.am index 6bca851..3d0c959 100644 --- a/build/tizen/adaptor/Makefile.am +++ b/build/tizen/adaptor/Makefile.am @@ -101,6 +101,9 @@ include ../../../adaptors/devel-api/file.list static_libraries_libunibreak_src_dir = ../../../text/dali/internal/libunibreak include ../../../text/dali/internal/libunibreak/file.list +static_libraries_glyphy_src_dir = ../../../text/dali/internal/glyphy +include ../../../text/dali/internal/glyphy/file.list + # Package doc package_doxy_dir = ../../../doc include ../../../doc/file.list @@ -245,7 +248,10 @@ main_loop_integration_src_files = $(adaptor_common_internal_ecore_src_files) input_event_handler_src_files = $(adaptor_ecore_x_event_handler_internal_src_files) endif - +if ENABLE_VECTOR_BASED_TEXT_RENDERING +adaptor_internal_src_files += $(static_libraries_glyphy_src_files) +DALI_ADAPTOR_CFLAGS += -DENABLE_VECTOR_BASED_TEXT_RENDERING +endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = dali-adaptor.pc dali-adaptor-integration.pc diff --git a/build/tizen/adaptor/configure.ac b/build/tizen/adaptor/configure.ac index 7fee548..a415c13 100644 --- a/build/tizen/adaptor/configure.ac +++ b/build/tizen/adaptor/configure.ac @@ -198,6 +198,9 @@ AM_CONDITIONAL([WAYLAND], [test x$enable_wayland = xyes]) AM_CONDITIONAL([USE_EFL], [test x$enable_efl = xyes]) AM_CONDITIONAL([USE_APPFW], [test x$enable_appfw = xyes]) +# Platforms with highp shader support can use vector based text +AM_CONDITIONAL([ENABLE_VECTOR_BASED_TEXT_RENDERING], [test x$enable_profile = xUBUNTU]) + AM_CONDITIONAL([ENABLE_NETWORK_LOGGING], [test x$enable_networklogging = xyes]) # Platforms should either enable features or remove them, they diff --git a/text/dali/devel-api/text-abstraction/font-client.cpp b/text/dali/devel-api/text-abstraction/font-client.cpp index 414c45c..3460dff 100644 --- a/text/dali/devel-api/text-abstraction/font-client.cpp +++ b/text/dali/devel-api/text-abstraction/font-client.cpp @@ -148,9 +148,9 @@ GlyphIndex FontClient::GetGlyphIndex( FontId fontId, Character charcode ) return GetImplementation(*this).GetGlyphIndex( fontId, charcode ); } -bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal, int desiredFixedSize ) +bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal, int desiredFixedSize ) { - return GetImplementation(*this).GetGlyphMetrics( array, size, horizontal, desiredFixedSize ); + return GetImplementation(*this).GetGlyphMetrics( array, size, type, horizontal, desiredFixedSize ); } BufferImage FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex ) @@ -158,6 +158,11 @@ BufferImage FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex ) return GetImplementation(*this).CreateBitmap( fontId, glyphIndex ); } +void FontClient::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight ) +{ + GetImplementation(*this).CreateVectorBlob( fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); +} + const GlyphInfo& FontClient::GetEllipsisGlyph( PointSize26Dot6 pointSize ) { return GetImplementation(*this).GetEllipsisGlyph( pointSize ); diff --git a/text/dali/devel-api/text-abstraction/font-client.h b/text/dali/devel-api/text-abstraction/font-client.h index a1ffcb5..2614cfc 100644 --- a/text/dali/devel-api/text-abstraction/font-client.h +++ b/text/dali/devel-api/text-abstraction/font-client.h @@ -290,14 +290,15 @@ public: * It may contain the advance and an offset set into the bearing from the shaping tool. * On return, the glyph's size value will be initialized. The bearing value will be updated by adding the font's glyph bearing to the one set by the shaping tool. * @param[in] size The size of the array. + * @param[in] type The type of glyphs used for rendering; either bitmaps or vectors. * @param[in] horizontal True for horizontal layouts (set to false for vertical layouting). * @param[in] desiredFixedSize The metrics for fixed-size fonts will be scaled to this desired size (in pixels). * @return True if all of the requested metrics were found. */ - bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal = true, int desiredFixedSize = 0 ); + bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal = true, int desiredFixedSize = 0 ); /** - * @brief Render a bitmap representation of a glyph. + * @brief Create a bitmap representation of a glyph. * * @param[in] fontId The ID of the font. * @param[in] glyphIndex The index of a glyph within the specified font. @@ -306,6 +307,24 @@ public: BufferImage CreateBitmap( FontId fontId, GlyphIndex glyphIndex ); /** + * @brief Create a vector representation of a glyph. + * + * @note This feature requires highp shader support and is not available on all platforms + * @param[in] fontId The ID of the font. + * @param[in] glyphIndex The index of a glyph within the specified font. + * @param[out] blob A blob of data; this is owned by FontClient and should be copied by the caller of CreateVectorData(). + * @param[out] blobLength The length of the blob data, or zero if the blob creation failed. + * @param[out] nominalWidth The width of the blob. + * @param[out] nominalHeight The height of the blob. + */ + void CreateVectorBlob( FontId fontId, + GlyphIndex glyphIndex, + VectorBlob*& blob, + unsigned int& blobLength, + unsigned int& nominalWidth, + unsigned int& nominalHeight ); + + /** * @brief Retrieves the ellipsis glyph for a requested point size. * * @param[in] pointSize The requested point size. diff --git a/text/dali/devel-api/text-abstraction/text-abstraction-definitions.h b/text/dali/devel-api/text-abstraction/text-abstraction-definitions.h index 1bbc1e4..a6be687 100644 --- a/text/dali/devel-api/text-abstraction/text-abstraction-definitions.h +++ b/text/dali/devel-api/text-abstraction/text-abstraction-definitions.h @@ -58,6 +58,20 @@ enum WORD_NO_BREAK = 1u, ///< Text can't be broken into a new word. }; +enum GlyphType +{ + BITMAP_GLYPH, ///< Glyph stored as pixels. + VECTOR_GLYPH ///< Glyph stored as vectors (scalable). This feature requires highp shader support and is not available on all platforms. +}; + +struct VectorBlob +{ + unsigned char r; + unsigned char g; + unsigned char b; + unsigned char a; +}; + } // namespace TextAbstraction } // namespace Dali diff --git a/text/dali/internal/glyphy/file.list b/text/dali/internal/glyphy/file.list new file mode 100644 index 0000000..6622a58 --- /dev/null +++ b/text/dali/internal/glyphy/file.list @@ -0,0 +1,9 @@ +# Add local source files here: + +static_libraries_glyphy_src_files = \ + $(static_libraries_glyphy_src_dir)/glyphy-arcs.cc \ + $(static_libraries_glyphy_src_dir)/glyphy-blob-impl.cc \ + $(static_libraries_glyphy_src_dir)/glyphy-extents.cc \ + $(static_libraries_glyphy_src_dir)/glyphy-outline.cc \ + $(static_libraries_glyphy_src_dir)/glyphy-sdf.cc \ + $(static_libraries_glyphy_src_dir)/vector-font-cache.cpp diff --git a/text/dali/internal/glyphy/glyphy-arc-bezier.hh b/text/dali/internal/glyphy/glyphy-arc-bezier.hh new file mode 100644 index 0000000..a3c71aa --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-arc-bezier.hh @@ -0,0 +1,228 @@ +/* + * Copyright 2012,2013 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju + */ + +#ifndef GLYPHY_ARC_BEZIER_HH +#define GLYPHY_ARC_BEZIER_HH + +#include "glyphy-common.hh" +#include "glyphy-geometry.hh" + +namespace GLyphy { +namespace ArcBezier { + +using namespace Geometry; + + +class MaxDeviationApproximatorExact +{ + public: + /* Returns 3 max(abs(d₀ t (1-t)² + d₁ t² (1-t)) for 0≤t≤1. */ + static double approximate_deviation (double d0, double d1) + { + double candidates[4] = {0,1}; + unsigned int num_candidates = 2; + if (d0 == d1) + candidates[num_candidates++] = .5; + else { + double delta = d0*d0 - d0*d1 + d1*d1; + double t2 = 1. / (3 * (d0 - d1)); + double t0 = (2 * d0 - d1) * t2; + if (delta == 0) + candidates[num_candidates++] = t0; + else if (delta > 0) { + /* This code can be optimized to avoid the sqrt if the solution + * is not feasible (ie. lies outside (0,1)). I have implemented + * that in cairo-spline.c:_cairo_spline_bound(). Can be reused + * here. + */ + double t1 = sqrt (delta) * t2; + candidates[num_candidates++] = t0 - t1; + candidates[num_candidates++] = t0 + t1; + } + } + + double e = 0; + for (unsigned int i = 0; i < num_candidates; i++) { + double t = candidates[i]; + double ee; + if (t < 0. || t > 1.) + continue; + ee = fabs (3 * t * (1-t) * (d0 * (1 - t) + d1 * t)); + e = std::max (e, ee); + } + + return e; + } +}; + + + +template +class ArcBezierErrorApproximatorBehdad +{ + public: + static double approximate_bezier_arc_error (const Bezier &b0, const Arc &a) + { + assert (b0.p0 == a.p0); + assert (b0.p3 == a.p1); + + double ea; + Bezier b1 = a.approximate_bezier (&ea); + + assert (b0.p0 == b1.p0); + assert (b0.p3 == b1.p3); + + Vector v0 = b1.p1 - b0.p1; + Vector v1 = b1.p2 - b0.p2; + + Vector b = (b0.p3 - b0.p0).normalized (); + v0 = v0.rebase (b); + v1 = v1.rebase (b); + + Vector v (MaxDeviationApproximator::approximate_deviation (v0.dx, v1.dx), + MaxDeviationApproximator::approximate_deviation (v0.dy, v1.dy)); + + /* Edge cases: If d*d is too close too large default to a weak bound. */ + if (a.d * a.d > 1. - 1e-4) + return ea + v.len (); + + /* If the wedge doesn't contain control points, default to weak bound. */ + if (!a.wedge_contains_point (b0.p1) || !a.wedge_contains_point (b0.p2)) + return ea + v.len (); + + /* If straight line, return the max ortho deviation. */ + if (fabs (a.d) < 1e-6) + return ea + v.dy; + + /* We made sure that fabs(a.d) < 1 */ + double tan_half_alpha = fabs (tan2atan (a.d)); + + double tan_v = v.dx / v.dy; + + double eb; + if (fabs (tan_v) <= tan_half_alpha) + return ea + v.len (); + + double c2 = (a.p1 - a.p0).len () * .5; + double r = a.radius (); + + eb = Vector (c2 + v.dx, c2 / tan_half_alpha + v.dy).len () - r; + assert (eb >= 0); + + return ea + eb; + } +}; + + + +template +class ArcBezierApproximatorMidpointSimple +{ + public: + static const Arc approximate_bezier_with_arc (const Bezier &b, double *error) + { + Arc a (b.p0, b.p3, b.midpoint (), false); + + *error = ArcBezierErrorApproximator::approximate_bezier_arc_error (b, a); + + return a; + } +}; + +template +class ArcBezierApproximatorMidpointTwoPart +{ + public: + static const Arc approximate_bezier_with_arc (const Bezier &b, double *error, double mid_t = .5) + { + Pair pair = b.split (mid_t); + Point m = pair.second.p0; + + Arc a0 (b.p0, m, b.p3, true); + Arc a1 (m, b.p3, b.p0, true); + + double e0 = ArcBezierErrorApproximator::approximate_bezier_arc_error (pair.first, a0); + double e1 = ArcBezierErrorApproximator::approximate_bezier_arc_error (pair.second, a1); + *error = std::max (e0, e1); + + return Arc (b.p0, b.p3, m, false); + } +}; + +template +class ArcBezierApproximatorQuantized +{ + public: + ArcBezierApproximatorQuantized (double _max_d = GLYPHY_INFINITY, unsigned int _d_bits = 0) : + max_d (_max_d), d_bits (_d_bits) {}; + + protected: + double max_d; + unsigned int d_bits; + + public: + const Arc approximate_bezier_with_arc (const Bezier &b, double *error) const + { + double mid_t = .5; + Arc a (b.p0, b.p3, b.point (mid_t), false); + Arc orig_a = a; + + if (isfinite (max_d)) { + assert (max_d >= 0); + if (fabs (a.d) > max_d) + a.d = a.d < 0 ? -max_d : max_d; + } + if (d_bits && max_d != 0) { + assert (isfinite (max_d)); + assert (fabs (a.d) <= max_d); + int mult = (1 << (d_bits - 1)) - 1; + int id = round (a.d / max_d * mult); + assert (-mult <= id && id <= mult); + a.d = id * max_d / mult; + assert (fabs (a.d) <= max_d); + } + + /* Error introduced by arc quantization */ + double ed = fabs (a.d - orig_a.d) * (a.p1 - a.p0).len () * .5; + + ArcBezierApproximatorMidpointTwoPart + ::approximate_bezier_with_arc (b, error, mid_t); + + if (ed) { + *error += ed; + + /* Try a simple one-arc approx which works with the quantized arc. + * May produce smaller error bound. */ + double e = ArcBezierErrorApproximator::approximate_bezier_arc_error (b, a); + if (e < *error) + *error = e; + } + + return a; + } +}; + +typedef MaxDeviationApproximatorExact MaxDeviationApproximatorDefault; +typedef ArcBezierErrorApproximatorBehdad ArcBezierErrorApproximatorDefault; +typedef ArcBezierApproximatorMidpointTwoPart ArcBezierApproximatorDefault; +typedef ArcBezierApproximatorQuantized ArcBezierApproximatorQuantizedDefault; + +} /* namespace ArcBezier */ +} /* namespace GLyphy */ + +#endif /* GLYPHY_ARC_BEZIER_HH */ diff --git a/text/dali/internal/glyphy/glyphy-arcs-bezier.hh b/text/dali/internal/glyphy/glyphy-arcs-bezier.hh new file mode 100644 index 0000000..28ebc6b --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-arcs-bezier.hh @@ -0,0 +1,133 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju + */ + +#ifndef GLYPHY_ARCS_BEZIER_HH +#define GLYPHY_ARCS_BEZIER_HH + +#include "glyphy-common.hh" +#include "glyphy-geometry.hh" +#include "glyphy-arc-bezier.hh" + +namespace GLyphy { +namespace ArcsBezier { + +using namespace Geometry; +using namespace ArcBezier; + +template +class ArcsBezierApproximatorSpringSystem +{ + static inline void calc_arcs (const Bezier &b, + const std::vector &t, + const ArcBezierApproximator &appx, + std::vector &e, + std::vector &arcs, + double &max_e, double &min_e) + { + unsigned int n = t.size () - 1; + e.resize (n); + arcs.clear (); + max_e = 0; + min_e = GLYPHY_INFINITY; + for (unsigned int i = 0; i < n; i++) + { + Bezier segment = b.segment (t[i], t[i + 1]); + arcs.push_back (appx.approximate_bezier_with_arc (segment, &e[i])); + + max_e = std::max (max_e, e[i]); + min_e = std::min (min_e, e[i]); + } + } + + static inline void jiggle (const Bezier &b, + const ArcBezierApproximator &appx, + std::vector &t, + std::vector &e, + std::vector &arcs, + double &max_e, double &min_e, + double tolerance, + unsigned int &n_jiggle) + { + unsigned int n = t.size () - 1; + unsigned int max_jiggle = log2 (n) + 1; + unsigned int s; + for (s = 0; s < max_jiggle; s++) + { + double total = 0; + for (unsigned int i = 0; i < n; i++) { + double l = t[i + 1] - t[i]; + double k_inv = l * pow (e[i], -.3); + total += k_inv; + e[i] = k_inv; + } + for (unsigned int i = 0; i < n; i++) { + double k_inv = e[i]; + double l = k_inv / total; + t[i + 1] = t[i] + l; + } + t[n] = 1.0; // Do this to get real 1.0, not .9999999999999998! + + calc_arcs (b, t, appx, e, arcs, max_e, min_e); + + n_jiggle++; + if (max_e < tolerance || (2 * min_e - max_e > tolerance)) + break; + } + } + + public: + static void approximate_bezier_with_arcs (const Bezier &b, + double tolerance, + const ArcBezierApproximator &appx, + std::vector &arcs, + double *perror, + unsigned int max_segments = 100) + { + std::vector t; + std::vector e; + double max_e, min_e; + unsigned int n_jiggle = 0; + + /* Technically speaking we can bsearch for n. */ + for (unsigned int n = 1; n <= max_segments; n++) + { + t.resize (n + 1); + for (unsigned int i = 0; i < n; i++) + t[i] = double (i) / n; + t[n] = 1.0; // Do this out of the loop to get real 1.0, not .9999999999999998! + + calc_arcs (b, t, appx, e, arcs, max_e, min_e); + + for (unsigned int i = 0; i < n; i++) + if (e[i] <= tolerance) { + jiggle (b, appx, t, e, arcs, max_e, min_e, tolerance, n_jiggle); + break; + } + + if (max_e <= tolerance) + break; + } + if (perror) + *perror = max_e; + } +}; + +} /* namespace ArcsBezier */ +} /* namespace GLyphy */ + +#endif /* GLYPHY_ARCS_BEZIER_HH */ diff --git a/text/dali/internal/glyphy/glyphy-arcs.cc b/text/dali/internal/glyphy/glyphy-arcs.cc new file mode 100644 index 0000000..be9e46d --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-arcs.cc @@ -0,0 +1,284 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glyphy-common.hh" +#include "glyphy-geometry.hh" +#include "glyphy-arcs-bezier.hh" + +using namespace GLyphy::Geometry; +using namespace GLyphy::ArcsBezier; + + + +/* + * Approximate outlines with multiple arcs + */ + + +struct glyphy_arc_accumulator_t { + unsigned int refcount; + + double tolerance; + unsigned int d_bits; + glyphy_arc_endpoint_accumulator_callback_t callback; + void *user_data; + + glyphy_point_t start_point; + glyphy_point_t current_point; + bool need_moveto; + unsigned int num_endpoints; + double max_error; + glyphy_bool_t success; +}; + + +glyphy_arc_accumulator_t * +glyphy_arc_accumulator_create (void) +{ + glyphy_arc_accumulator_t *acc = (glyphy_arc_accumulator_t *) calloc (1, sizeof (glyphy_arc_accumulator_t)); + acc->refcount = 1; + + acc->tolerance = 5e-4; + acc->d_bits = 8; + acc->callback = NULL; + acc->user_data = NULL; + + glyphy_arc_accumulator_reset (acc); + + return acc; +} + +void +glyphy_arc_accumulator_reset (glyphy_arc_accumulator_t *acc) +{ + acc->start_point = acc->current_point = Point (0, 0); + acc->need_moveto = true; + acc->num_endpoints = 0; + acc->max_error = 0; + acc->success = true; +} + +void +glyphy_arc_accumulator_destroy (glyphy_arc_accumulator_t *acc) +{ + if (!acc || --acc->refcount) + return; + + free (acc); +} + +/* Configure acc */ + +void +glyphy_arc_accumulator_set_tolerance (glyphy_arc_accumulator_t *acc, + double tolerance) +{ + acc->tolerance = tolerance; +} + +double +glyphy_arc_accumulator_get_tolerance (glyphy_arc_accumulator_t *acc) +{ + return acc->tolerance; +} + +void +glyphy_arc_accumulator_set_callback (glyphy_arc_accumulator_t *acc, + glyphy_arc_endpoint_accumulator_callback_t callback, + void *user_data) +{ + acc->callback = callback; + acc->user_data = user_data; +} + +void +glyphy_arc_accumulator_get_callback (glyphy_arc_accumulator_t *acc, + glyphy_arc_endpoint_accumulator_callback_t *callback, + void **user_data) +{ + *callback = acc->callback; + *user_data = acc->user_data; +} + +/* Accumulation results */ + +double +glyphy_arc_accumulator_get_error (glyphy_arc_accumulator_t *acc) +{ + return acc->max_error; +} + +glyphy_bool_t +glyphy_arc_accumulator_successful (glyphy_arc_accumulator_t *acc) +{ + return acc->success; +} + + +/* Accumulate */ + +static void +emit (glyphy_arc_accumulator_t *acc, const Point &p, double d) +{ + glyphy_arc_endpoint_t endpoint = {p, d}; + acc->success = acc->success && acc->callback (&endpoint, acc->user_data); + if (acc->success) { + acc->num_endpoints++; + acc->current_point = p; + } +} + +static void +accumulate (glyphy_arc_accumulator_t *acc, const Point &p, double d) +{ + if (Point (acc->current_point) == p) + return; + if (d == GLYPHY_INFINITY) { + /* Emit moveto lazily, for cleaner outlines */ + acc->need_moveto = true; + acc->current_point = p; + return; + } + if (acc->need_moveto) { + emit (acc, acc->current_point, GLYPHY_INFINITY); + if (acc->success) { + acc->start_point = acc->current_point; + acc->need_moveto = false; + } + } + emit (acc, p, d); +} + +static void +move_to (glyphy_arc_accumulator_t *acc, const Point &p) +{ + if (!acc->num_endpoints || p != acc->current_point) + accumulate (acc, p, GLYPHY_INFINITY); +} + +static void +arc_to (glyphy_arc_accumulator_t *acc, const Point &p1, double d) +{ + accumulate (acc, p1, d); +} + +static void +bezier (glyphy_arc_accumulator_t *acc, const Bezier &b) +{ + double e; + + std::vector arcs; + typedef ArcBezierApproximatorQuantizedDefault _ArcBezierApproximator; + _ArcBezierApproximator appx (GLYPHY_MAX_D, acc->d_bits); + ArcsBezierApproximatorSpringSystem<_ArcBezierApproximator> + ::approximate_bezier_with_arcs (b, acc->tolerance, appx, arcs, &e); + + acc->max_error = std::max (acc->max_error, e); + + move_to (acc, b.p0); + for (unsigned int i = 0; i < arcs.size (); i++) + arc_to (acc, arcs[i].p1, arcs[i].d); +} + +static void +close_path (glyphy_arc_accumulator_t *acc) +{ + if (!acc->need_moveto && Point (acc->current_point) != Point (acc->start_point)) + arc_to (acc, acc->start_point, 0); +} + +void +glyphy_arc_accumulator_move_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p0) +{ + move_to (acc, *p0); +} + +void +glyphy_arc_accumulator_line_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1) +{ + arc_to (acc, *p1, 0); +} + +void +glyphy_arc_accumulator_conic_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1, + const glyphy_point_t *p2) +{ + bezier (acc, Bezier (acc->current_point, + Point (acc->current_point).lerp (2/3., *p1), + Point (*p2).lerp (2/3., *p1), + *p2)); +} + +void +glyphy_arc_accumulator_cubic_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1, + const glyphy_point_t *p2, + const glyphy_point_t *p3) +{ + bezier (acc, Bezier (acc->current_point, *p1, *p2, *p3)); +} + +void +glyphy_arc_accumulator_arc_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1, + double d) +{ + arc_to (acc, *p1, d); +} + +void +glyphy_arc_accumulator_close_path (glyphy_arc_accumulator_t *acc) +{ + close_path (acc); +} + + + +/* + * Outline extents from arc list + */ + + +void +glyphy_arc_list_extents (const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + glyphy_extents_t *extents) +{ + Point p0 (0, 0); + glyphy_extents_clear (extents); + for (unsigned int i = 0; i < num_endpoints; i++) { + const glyphy_arc_endpoint_t &endpoint = endpoints[i]; + if (endpoint.d == GLYPHY_INFINITY) { + p0 = endpoint.p; + continue; + } + Arc arc (p0, endpoint.p, endpoint.d); + p0 = endpoint.p; + + glyphy_extents_t arc_extents; + arc.extents (arc_extents); + glyphy_extents_extend (extents, &arc_extents); + } +} diff --git a/text/dali/internal/glyphy/glyphy-blob-impl.cc b/text/dali/internal/glyphy/glyphy-blob-impl.cc new file mode 100644 index 0000000..84caf28 --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-blob-impl.cc @@ -0,0 +1,328 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glyphy-common.hh" +#include "glyphy-geometry.hh" + +#define GRID_SIZE 24 + +using namespace GLyphy::Geometry; + + +#define UPPER_BITS(v,bits,total_bits) ((v) >> ((total_bits) - (bits))) +#define LOWER_BITS(v,bits,total_bits) ((v) & ((1 << (bits)) - 1)) + +#define MAX_X 4095 +#define MAX_Y 4095 + +static inline glyphy_rgba_t +arc_endpoint_encode (unsigned int ix, unsigned int iy, double d) +{ + glyphy_rgba_t v; + + /* 12 bits for each of x and y, 8 bits for d */ + assert (ix <= MAX_X); + assert (iy <= MAX_Y); + unsigned int id; + if (isinf (d)) + id = 0; + else { + assert (fabs (d) <= GLYPHY_MAX_D); + id = 128 + lround (d * 127 / GLYPHY_MAX_D); + } + assert (id < 256); + + v.r = id; + v.g = LOWER_BITS (ix, 8, 12); + v.b = LOWER_BITS (iy, 8, 12); + v.a = ((ix >> 8) << 4) | (iy >> 8); + return v; +} + +static inline glyphy_rgba_t +arc_list_encode (unsigned int offset, unsigned int num_points, int side) +{ + glyphy_rgba_t v; + v.r = 0; // unused for arc-list encoding + v.g = UPPER_BITS (offset, 8, 16); + v.b = LOWER_BITS (offset, 8, 16); + v.a = LOWER_BITS (num_points, 8, 8); + if (side < 0 && !num_points) + v.a = 255; + return v; +} + +static inline glyphy_rgba_t +line_encode (const Line &line) +{ + Line l = line.normalized (); + double angle = l.n.angle (); + double distance = l.c; + + int ia = lround (-angle / M_PI * 0x7FFF); + unsigned int ua = ia + 0x8000; + assert (0 == (ua & ~0xFFFF)); + + int id = lround (distance * 0x1FFF); + unsigned int ud = id + 0x4000; + assert (0 == (ud & ~0x7FFF)); + + /* Marker for line-encoded */ + ud |= 0x8000; + + glyphy_rgba_t v; + v.r = ud >> 8; + v.g = ud & 0xFF; + v.b = ua >> 8; + v.a = ua & 0xFF; + return v; +} + + +/* Given a cell, fills the vector closest_arcs with arcs that may be closest to some point in the cell. + * Uses idea that all close arcs to cell must be ~close to center of cell. + */ +static void +closest_arcs_to_cell (Point c0, Point c1, /* corners */ + double faraway, + const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + std::vector &near_endpoints, + int *side) +{ + // Find distance between cell center + Point c = c0.midpoint (c1); + double min_dist = glyphy_sdf_from_arc_list (endpoints, num_endpoints, &c, NULL); + + *side = min_dist >= 0 ? +1 : -1; + min_dist = fabs (min_dist); + std::vector near_arcs; + + // If d is the distance from the center of the square to the nearest arc, then + // all nearest arcs to the square must be at most almost [d + half_diagonal] from the center. + double half_diagonal = (c - c0).len (); + double radius_squared = pow (min_dist + half_diagonal, 2); + if (min_dist - half_diagonal <= faraway) { + Point p0 (0, 0); + for (unsigned int i = 0; i < num_endpoints; i++) { + const glyphy_arc_endpoint_t &endpoint = endpoints[i]; + if (endpoint.d == GLYPHY_INFINITY) { + p0 = endpoint.p; + continue; + } + Arc arc (p0, endpoint.p, endpoint.d); + p0 = endpoint.p; + + if (arc.squared_distance_to_point (c) <= radius_squared) + near_arcs.push_back (arc); + } + } + + Point p1 = Point (0, 0); + for (unsigned i = 0; i < near_arcs.size (); i++) + { + Arc arc = near_arcs[i]; + + if (i == 0 || p1 != arc.p0) { + glyphy_arc_endpoint_t endpoint = {arc.p0, GLYPHY_INFINITY}; + near_endpoints.push_back (endpoint); + p1 = arc.p0; + } + + glyphy_arc_endpoint_t endpoint = {arc.p1, arc.d}; + near_endpoints.push_back (endpoint); + p1 = arc.p1; + } +} + + +glyphy_bool_t +glyphy_arc_list_encode_blob (const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + glyphy_rgba_t *blob, + unsigned int blob_size, + double faraway, + double avg_fetch_desired, + double *avg_fetch_achieved, + unsigned int *output_len, + unsigned int *nominal_width, /* 8bit */ + unsigned int *nominal_height, /* 8bit */ + glyphy_extents_t *pextents) +{ + glyphy_extents_t extents; + glyphy_extents_clear (&extents); + + glyphy_arc_list_extents (endpoints, num_endpoints, &extents); + + if (glyphy_extents_is_empty (&extents)) { + *pextents = extents; + if (!blob_size) + return false; + *blob = arc_list_encode (0, 0, +1); + *avg_fetch_achieved = 1; + *output_len = 1; + *nominal_width = *nominal_height = 1; + return true; + } + + /* Add antialiasing padding */ + extents.min_x -= faraway; + extents.min_y -= faraway; + extents.max_x += faraway; + extents.max_y += faraway; + + double glyph_width = extents.max_x - extents.min_x; + double glyph_height = extents.max_y - extents.min_y; + double unit = std::max (glyph_width, glyph_height); + + unsigned int grid_w = GRID_SIZE; + unsigned int grid_h = GRID_SIZE; + + if (glyph_width > glyph_height) { + while ((grid_h - 1) * unit / grid_w > glyph_height) + grid_h--; + glyph_height = grid_h * unit / grid_w; + extents.max_y = extents.min_y + glyph_height; + } else { + while ((grid_w - 1) * unit / grid_h > glyph_width) + grid_w--; + glyph_width = grid_w * unit / grid_h; + extents.max_x = extents.min_x + glyph_width; + } + + double cell_unit = unit / std::max (grid_w, grid_h); + + std::vector tex_data; + std::vector near_endpoints; + + unsigned int header_length = grid_w * grid_h; + unsigned int offset = header_length; + tex_data.resize (header_length); + Point origin = Point (extents.min_x, extents.min_y); + unsigned int total_arcs = 0; + + for (unsigned int row = 0; row < grid_h; row++) + for (unsigned int col = 0; col < grid_w; col++) + { + Point cp0 = origin + Vector ((col + 0) * cell_unit, (row + 0) * cell_unit); + Point cp1 = origin + Vector ((col + 1) * cell_unit, (row + 1) * cell_unit); + near_endpoints.clear (); + + int side; + closest_arcs_to_cell (cp0, cp1, + faraway, + endpoints, num_endpoints, + near_endpoints, + &side); + +#define QUANTIZE_X(X) (lround (MAX_X * ((X - extents.min_x) / glyph_width ))) +#define QUANTIZE_Y(Y) (lround (MAX_Y * ((Y - extents.min_y) / glyph_height))) +#define DEQUANTIZE_X(X) (double (X) / MAX_X * glyph_width + extents.min_x) +#define DEQUANTIZE_Y(Y) (double (Y) / MAX_Y * glyph_height + extents.min_y) +#define SNAP(P) (Point (DEQUANTIZE_X (QUANTIZE_X ((P).x)), DEQUANTIZE_Y (QUANTIZE_Y ((P).y)))) + + if (near_endpoints.size () == 2 && near_endpoints[1].d == 0) { + Point c (extents.min_x + glyph_width * .5, extents.min_y + glyph_height * .5); + Line line (SNAP (near_endpoints[0].p), SNAP (near_endpoints[1].p)); + line.c -= line.n * Vector (c); + line.c /= unit; + tex_data[row * grid_w + col] = line_encode (line); + continue; + } + + /* If the arclist is two arcs that can be combined in encoding if reordered, + * do that. */ + if (near_endpoints.size () == 4 && + isinf (near_endpoints[2].d) && + near_endpoints[0].p.x == near_endpoints[3].p.x && + near_endpoints[0].p.y == near_endpoints[3].p.y) + { + glyphy_arc_endpoint_t e0, e1, e2; + e0 = near_endpoints[2]; + e1 = near_endpoints[3]; + e2 = near_endpoints[1]; + near_endpoints.resize (0); + near_endpoints.push_back (e0); + near_endpoints.push_back (e1); + near_endpoints.push_back (e2); + } + + for (unsigned i = 0; i < near_endpoints.size (); i++) { + glyphy_arc_endpoint_t &endpoint = near_endpoints[i]; + tex_data.push_back (arc_endpoint_encode (QUANTIZE_X(endpoint.p.x), QUANTIZE_Y(endpoint.p.y), endpoint.d)); + } + + unsigned int current_endpoints = tex_data.size () - offset; + + if (current_endpoints) + { + /* See if we can fulfill this cell by using already-encoded arcs */ + const glyphy_rgba_t *needle = &tex_data[offset]; + unsigned int needle_len = current_endpoints; + const glyphy_rgba_t *haystack = &tex_data[header_length]; + unsigned int haystack_len = offset - header_length; + + bool found = false; + while (haystack_len >= needle_len) { + /* Trick: we don't care about first endpoint's d value, so skip one + * byte in comparison. This works because arc_encode() packs the + * d value in the first byte. */ + if (0 == memcmp (1 + (const char *) needle, + 1 + (const char *) haystack, + needle_len * sizeof (*needle) - 1)) { + found = true; + break; + } + haystack++; + haystack_len--; + } + if (found) { + unsigned int new_offset = haystack - &tex_data[0]; + tex_data.resize (offset); + haystack = needle = NULL; /* Invalidated by the resize. */ + offset = new_offset; + } + } + else + offset = 0; + + tex_data[row * grid_w + col] = arc_list_encode (offset, current_endpoints, side); + offset = tex_data.size (); + + total_arcs += current_endpoints; + } + + if (avg_fetch_achieved) + *avg_fetch_achieved = 1 + double (total_arcs) / (grid_w * grid_h); + + *pextents = extents; + + if (tex_data.size () > blob_size) + return false; + + memcpy (blob, &tex_data[0], tex_data.size () * sizeof(tex_data[0])); + *output_len = tex_data.size (); + *nominal_width = grid_w; + *nominal_height = grid_h; + + return true; +} diff --git a/text/dali/internal/glyphy/glyphy-common.hh b/text/dali/internal/glyphy/glyphy-common.hh new file mode 100644 index 0000000..8d4d02c --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-common.hh @@ -0,0 +1,59 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju + */ + +#ifndef GLYPHY_COMMON_HH +#define GLYPHY_COMMON_HH + +#include "glyphy.h" + +#include +#include +#include +#include +#include +#include + +#ifndef GLYPHY_EPSILON +# define GLYPHY_EPSILON 1e-5 +#endif +#ifndef GLYPHY_INFINITY +# define GLYPHY_INFINITY INFINITY +#endif + + +static inline bool +iszero (double v) +{ + return fabs (v) < 2 * GLYPHY_EPSILON; +} + + +#define GLYPHY_MAX_D .5 + +#undef ARRAY_LENGTH +#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) + +#define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1] +#define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond)) +#define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond)) + +#ifdef __ANDROID__ +#define log2(x) (log(x) / log(2.0)) +#endif + +#endif /* GLYPHY_COMMON_HH */ diff --git a/text/dali/internal/glyphy/glyphy-extents.cc b/text/dali/internal/glyphy/glyphy-extents.cc new file mode 100644 index 0000000..bccc861 --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-extents.cc @@ -0,0 +1,89 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glyphy-common.hh" + + +void +glyphy_extents_clear (glyphy_extents_t *extents) +{ + extents->min_x = GLYPHY_INFINITY; + extents->min_y = GLYPHY_INFINITY; + extents->max_x = -GLYPHY_INFINITY; + extents->max_y = -GLYPHY_INFINITY; +} + +glyphy_bool_t +glyphy_extents_is_empty (const glyphy_extents_t *extents) +{ + return isinf (extents->min_x); +} + +void +glyphy_extents_add (glyphy_extents_t *extents, + const glyphy_point_t *p) +{ + if (glyphy_extents_is_empty (extents)) { + extents->min_x = extents->max_x = p->x; + extents->min_y = extents->max_y = p->y; + return; + } + extents->min_x = std::min (extents->min_x, p->x); + extents->min_y = std::min (extents->min_y, p->y); + extents->max_x = std::max (extents->max_x, p->x); + extents->max_y = std::max (extents->max_y, p->y); +} + +void +glyphy_extents_extend (glyphy_extents_t *extents, + const glyphy_extents_t *other) +{ + if (glyphy_extents_is_empty (other)) + return; + if (glyphy_extents_is_empty (extents)) { + *extents = *other; + return; + } + extents->min_x = std::min (extents->min_x, other->min_x); + extents->min_y = std::min (extents->min_y, other->min_y); + extents->max_x = std::max (extents->max_x, other->max_x); + extents->max_y = std::max (extents->max_y, other->max_y); +} + +glyphy_bool_t +glyphy_extents_includes (const glyphy_extents_t *extents, + const glyphy_point_t *p) +{ + return extents->min_x <= p->x && p->x <= extents->max_x && + extents->min_y <= p->y && p->y <= extents->max_y; +} + +void +glyphy_extents_scale (glyphy_extents_t *extents, + double x_scale, + double y_scale) +{ + extents->min_x *= x_scale; + extents->max_x *= x_scale; + extents->min_y *= y_scale; + extents->max_y *= y_scale; +} diff --git a/text/dali/internal/glyphy/glyphy-freetype.h b/text/dali/internal/glyphy/glyphy-freetype.h new file mode 100644 index 0000000..6105e88 --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-freetype.h @@ -0,0 +1,90 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju + */ + +/* Intentionally doesn't have include guards */ + +#include "glyphy.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include FT_FREETYPE_H +#include FT_OUTLINE_H + +static int +glyphy_freetype_move_to(FT_Vector *to, + glyphy_arc_accumulator_t *acc) +{ + glyphy_point_t p1 = {static_cast(to->x), static_cast(to->y)}; + glyphy_arc_accumulator_close_path (acc); + glyphy_arc_accumulator_move_to (acc, &p1); + return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; +} + +static int +glyphy_freetype_line_to(FT_Vector *to, + glyphy_arc_accumulator_t *acc) +{ + glyphy_point_t p1 = {static_cast(to->x), static_cast(to->y)}; + glyphy_arc_accumulator_line_to (acc, &p1); + return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; +} + +static int +glyphy_freetype_conic_to(FT_Vector *control, FT_Vector *to, + glyphy_arc_accumulator_t *acc) +{ + glyphy_point_t p1 = {static_cast(control->x), static_cast(control->y)}; + glyphy_point_t p2 = {static_cast(to->x), static_cast(to->y)}; + glyphy_arc_accumulator_conic_to (acc, &p1, &p2); + return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; +} + +static int +glyphy_freetype_cubic_to(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, + glyphy_arc_accumulator_t *acc) +{ + glyphy_point_t p1 = {static_cast(control1->x), static_cast(control1->y)}; + glyphy_point_t p2 = {static_cast(control2->x), static_cast(control2->y)}; + glyphy_point_t p3 = {static_cast(to->x), static_cast(to->y)}; + glyphy_arc_accumulator_cubic_to (acc, &p1, &p2, &p3); + return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; +} + +static FT_Error +glyphy_freetype_outline_decompose(const FT_Outline *outline, + glyphy_arc_accumulator_t *acc) +{ + const FT_Outline_Funcs outline_funcs = { + (FT_Outline_MoveToFunc) glyphy_freetype_move_to, + (FT_Outline_LineToFunc) glyphy_freetype_line_to, + (FT_Outline_ConicToFunc) glyphy_freetype_conic_to, + (FT_Outline_CubicToFunc) glyphy_freetype_cubic_to, + 0, /* shift */ + 0, /* delta */ + }; + + return FT_Outline_Decompose ((FT_Outline *) outline, &outline_funcs, acc); +} + +#ifdef __cplusplus +} +#endif diff --git a/text/dali/internal/glyphy/glyphy-geometry.hh b/text/dali/internal/glyphy/glyphy-geometry.hh new file mode 100644 index 0000000..3828676 --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-geometry.hh @@ -0,0 +1,713 @@ +/* + * Copyright 2012,2013 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju + */ + +#ifndef GLYPHY_GEOMETRY_HH +#define GLYPHY_GEOMETRY_HH + +#include "glyphy-common.hh" + +namespace GLyphy { +namespace Geometry { + +template struct Pair; +struct Vector; +struct SignedVector; +struct Point; +struct Line; +struct Segment; +struct Arc; +struct Bezier; + +/* returns tan (2 * atan (d)) */ +inline double tan2atan (double d) { return 2 * d / (1 - d*d); } + +/* returns sin (2 * atan (d)) */ +inline double sin2atan (double d) { return 2 * d / (1 + d*d); } + +/* returns cos (2 * atan (d)) */ +inline double cos2atan (double d) { return (1 - d*d) / (1 + d*d); } + +template +struct Pair { + typedef Type ElementType; + + inline Pair (const Type &first_, const Type &second_) : first (first_), second (second_) {} + + Type first, second; +}; + +struct Point : glyphy_point_t { + inline Point (double x_, double y_) { x = x_; y = y_; } + inline explicit Point (const Vector &v); + inline Point (const glyphy_point_t &p) { *(glyphy_point_t *)this = p; } + + inline bool operator == (const Point &p) const; + inline bool operator != (const Point &p) const; + inline Point& operator+= (const Vector &v); + inline Point& operator-= (const Vector &v); + inline const Point operator+ (const Vector &v) const; + inline const Point operator- (const Vector &v) const; + inline const Vector operator- (const Point &p) const; + inline const Point midpoint (const Point &p) const; + inline const Line bisector (const Point &p) const; + inline double distance_to_point (const Point &p) const; /* distance to point! */ + inline double squared_distance_to_point (const Point &p) const; /* square of distance to point! */ + + inline bool is_finite (void) const; + inline const Point lerp (const double &a, const Point &p) const; +}; + +struct Vector { + inline Vector (double dx_, double dy_) : dx (dx_), dy (dy_) {} + inline explicit Vector (const Point &p) : dx (p.x), dy (p.y) {} + + inline bool operator == (const Vector &v) const; + inline bool operator != (const Vector &v) const; + inline const Vector operator+ (void) const; + inline const Vector operator- (void) const; + inline Vector& operator+= (const Vector &v); + inline Vector& operator-= (const Vector &v); + inline Vector& operator*= (const double &s); + inline Vector& operator/= (const double &s); + inline const Vector operator+ (const Vector &v) const; + inline const Vector operator- (const Vector &v) const; + inline const Vector operator* (const double &s) const; + inline const Vector operator/ (const double &s) const; + inline double operator* (const Vector &v) const; /* dot product */ + inline const Point operator+ (const Point &p) const; + + inline bool is_nonzero (void) const; + inline double len (void) const; + inline double len2 (void) const; + inline const Vector normalized (void) const; + inline const Vector ortho (void) const; + inline const Vector normal (void) const; /* ortho().normalized() */ + inline double angle (void) const; + + inline const Vector rebase (const Vector &bx, const Vector &by) const; + inline const Vector rebase (const Vector &bx) const; + + double dx, dy; +}; + +struct SignedVector : Vector { + inline SignedVector (const Vector &v, bool negative_) : Vector (v), negative (negative_) {} + + inline bool operator == (const SignedVector &v) const; + inline bool operator != (const SignedVector &v) const; + inline const SignedVector operator- (void) const; + + bool negative; +}; + +struct Line { + inline Line (double a_, double b_, double c_) : n (a_, b_), c (c_) {} + inline Line (Vector n_, double c_) : n (n_), c (c_) {} + inline Line (const Point &p0, const Point &p1) : + n ((p1 - p0).ortho ()), c (n * Vector (p0)) {} + + inline const Point operator+ (const Line &l) const; /* line intersection! */ + inline const SignedVector operator- (const Point &p) const; /* shortest vector from point to line */ + + + inline const Line normalized (void) const; + inline const Vector normal (void) const; + + Vector n; /* line normal */ + double c; /* n.dx*x + n.dy*y = c */ +}; + +struct Segment { + inline Segment (const Point &p0_, const Point &p1_) : + p0 (p0_), p1 (p1_) {} + + inline const SignedVector operator- (const Point &p) const; /* shortest vector from point to ***line*** */ + inline double distance_to_point (const Point &p) const; /* shortest distance from point to segment */ + inline double squared_distance_to_point (const Point &p) const; /* shortest distance squared from point to segment */ + inline bool contains_in_span (const Point &p) const; /* is p in the stripe formed by sliding this segment? */ + inline double max_distance_to_arc (const Arc &a) const; + + + Point p0; + Point p1; +}; + + + +struct Arc { + inline Arc (const Point &p0_, const Point &p1_, const Point &pm, bool complement) : + p0 (p0_), p1 (p1_), + d (p0_ == pm || p1_ == pm ? 0 : + tan (((p1_-pm).angle () - (p0_-pm).angle ()) / 2 - (complement ? 0 : M_PI_2))) {} + inline Arc (const Point &p0_, const Point &p1_, const double &d_) : + p0 (p0_), p1 (p1_), d (d_) {} + inline Arc (const Point ¢er, double radius, const double &a0, const double &a1, bool complement) : + p0 (center + Vector (cos(a0),sin(a0)) * radius), + p1 (center + Vector (cos(a1),sin(a1)) * radius), + d (tan ((a1 - a0) / 4 - (complement ? 0 : M_PI_2))) {} + inline Arc (const glyphy_arc_t &a) : p0 (a.p0), p1 (a.p1), d (a.d) {} + inline operator glyphy_arc_t (void) const { glyphy_arc_t a = {p0, p1, d}; return a; } + + inline bool operator == (const Arc &a) const; + inline bool operator != (const Arc &a) const; + inline const SignedVector operator- (const Point &p) const; /* shortest vector from point to arc */ + + inline double radius (void) const; + inline const Point center (void) const; + inline const Pair tangents (void) const; + + inline Bezier approximate_bezier (double *error) const; + + inline bool wedge_contains_point (const Point &p) const; + inline double distance_to_point (const Point &p) const; + inline double squared_distance_to_point (const Point &p) const; + inline double extended_dist (const Point &p) const; + + inline void extents (glyphy_extents_t &extents) const; + + Point p0, p1; + double d; /* Depth */ +}; + +struct Bezier { + inline Bezier (const Point &p0_, const Point &p1_, + const Point &p2_, const Point &p3_) : + p0 (p0_), p1 (p1_), p2 (p2_), p3 (p3_) {} + + inline const Point point (const double &t) const; + inline const Point midpoint (void) const; + inline const Vector tangent (const double &t) const; + inline const Vector d_tangent (const double &t) const; + inline double curvature (const double &t) const; + inline const Pair split (const double &t) const; + inline const Pair halve (void) const; + inline const Bezier segment (const double &t0, const double &t1) const; + + Point p0, p1, p2, p3; +}; + + +/* Implementations */ + + +/* Point */ + +inline Point::Point (const Vector &v) { + x = v.dx; + y = v.dy; +} +inline bool Point::operator == (const Point &p) const { + return x == p.x && y == p.y; +} +inline bool Point::operator != (const Point &p) const { + return !(*this == p); +} +inline Point& Point::operator+= (const Vector &v) { + x += v.dx; + y += v.dy; + return *this; +} +inline Point& Point::operator-= (const Vector &v) { + x -= v.dx; + y -= v.dy; + return *this; +} +inline const Point Point::operator+ (const Vector &v) const { + return Point (*this) += v; +} +inline const Point Point::operator- (const Vector &v) const { + return Point (*this) -= v; +} +inline const Vector Point::operator- (const Point &p) const { + return Vector (x - p.x, y - p.y); +} + +inline const Point Point::midpoint (const Point &p) const { + return *this + (p - *this) / 2; +} +inline const Line Point::bisector (const Point &p) const { + Vector d = p - *this; + return Line (d.dx * 2, d.dy * 2, d * Vector (p) + d * Vector (*this)); +} + +inline double Point::distance_to_point (const Point &p) const { + return ((*this) - p).len (); +} + +inline double Point::squared_distance_to_point (const Point &p) const { + return ((*this) - p).len2 (); +} + +inline bool Point::is_finite (void) const { + return isfinite (x) && isfinite (y); +} +inline const Point Point::lerp (const double &a, const Point &p) const { + /* The following two cases are special-cased to get better floating + * point stability. We require that points that are the same be + * bit-equal. */ + if (a == 0) return *this; + if (a == 1.0) return p; + return Point ((1-a) * x + a * p.x, (1-a) * y + a * p.y); +} + + +/* Vector */ + +inline bool Vector::operator == (const Vector &v) const { + return dx == v.dx && dy == v.dy; +} +inline bool Vector::operator != (const Vector &v) const { + return !(*this == v); +} +inline const Vector Vector::operator+ (void) const { + return *this; +} +inline const Vector Vector::operator- (void) const { + return Vector (-dx, -dy); +} +inline Vector& Vector::operator+= (const Vector &v) { + dx += v.dx; + dy += v.dy; + return *this; +} +inline Vector& Vector::operator-= (const Vector &v) { + dx -= v.dx; + dy -= v.dy; + return *this; +} +inline Vector& Vector::operator*= (const double &s) { + dx *= s; + dy *= s; + return *this; +} +inline Vector& Vector::operator/= (const double &s) { + dx /= s; + dy /= s; + return *this; +} +inline const Vector Vector::operator+ (const Vector &v) const { + return Vector (*this) += v; +} +inline const Vector Vector::operator- (const Vector &v) const { + return Vector (*this) -= v; +} +inline const Vector Vector::operator* (const double &s) const { + return Vector (*this) *= s; +} +inline const Vector operator* (const double &s, const Vector &v) { + return v * s; +} +inline const Vector Vector::operator/ (const double &s) const { + return Vector (*this) /= s; +} +inline double Vector::operator* (const Vector &v) const { /* dot product */ + return dx * v.dx + dy * v.dy; +} +inline const Point Vector::operator+ (const Point &p) const { + return p + *this; +} + +inline bool Vector::is_nonzero (void) const { + return dx || dy; +} +inline double Vector::len (void) const { + return hypot (dx, dy); +} +inline double Vector::len2 (void) const { + return dx * dx + dy * dy; +} +inline const Vector Vector::normalized (void) const { + double d = len (); + return d ? *this / d : *this; +} +inline const Vector Vector::ortho (void) const { + return Vector (-dy, dx); +} +inline const Vector Vector::normal (void) const { + return ortho ().normalized (); +} +inline double Vector::angle (void) const { + return atan2 (dy, dx); +} + +inline const Vector Vector::rebase (const Vector &bx, + const Vector &by) const { + return Vector (*this * bx, *this * by); +} +inline const Vector Vector::rebase (const Vector &bx) const { + return rebase (bx, bx.ortho ()); +} + + +/* SignedVector */ + +inline bool SignedVector::operator == (const SignedVector &v) const { + return (const Vector &)(*this) == (const Vector &)(v) && negative == v.negative; +} +inline bool SignedVector::operator != (const SignedVector &v) const { + return !(*this == v); +} +inline const SignedVector SignedVector::operator- (void) const { + return SignedVector (-(const Vector &)(*this), !negative); +} + + +/* Line */ + +inline const Point Line::operator+ (const Line &l) const { + double det = n.dx * l.n.dy - n.dy * l.n.dx; + if (!det) + return Point (GLYPHY_INFINITY, GLYPHY_INFINITY); + return Point ((c * l.n.dy - n.dy * l.c) / det, + (n.dx * l.c - c * l.n.dx) / det); +} +inline const SignedVector Line::operator- (const Point &p) const { + double mag = -(n * Vector (p) - c) / n.len (); + return SignedVector (n.normalized () * mag, mag < 0); /******************************************************************************************* FIX. *************************************/ +} + +inline const SignedVector operator- (const Point &p, const Line &l) { + return -(l - p); +} + +inline const Line Line::normalized (void) const { + double d = n.len (); + return d ? Line (n / d, c / d) : *this; +} +inline const Vector Line::normal (void) const { + return n; +} + +/* Segment */ +inline const SignedVector Segment::operator- (const Point &p) const { + /* shortest vector from point to line */ + return p - Line (p1, p0); /************************************************************************************************** Should the order (p1, p0) depend on d?? ***********************/ +} + +/* Segment */ +inline bool Segment::contains_in_span (const Point &p) const { + if (p0 == p1) + return false; + + /* shortest vector from point to line */ + Line temp (p0, p1); + double mag = -(temp.n * Vector (p) - temp.c) / temp.n.len (); + Vector y (temp.n.normalized () * mag); + Point z = y + p; + + // Check if z is between p0 and p1. + + if (fabs (p1.y - p0.y) > fabs (p1.x - p0.x)) { + return ((z.y - p0.y > 0 && p1.y - p0.y > z.y - p0.y) || + (z.y - p0.y < 0 && p1.y - p0.y < z.y - p0.y)); + } + else { + return ((0 < z.x - p0.x && z.x - p0.x < p1.x - p0.x) || + (0 > z.x - p0.x && z.x - p0.x > p1.x - p0.x)); + } +} + +inline double Segment::distance_to_point (const Point &p) const { + if (p0 == p1) + return 0; + + // Check if z is between p0 and p1. + Line temp (p0, p1); + if (contains_in_span (p)) + return -(temp.n * Vector (p) - temp.c) / temp.n.len (); + + double dist_p_p0 = p.distance_to_point (p0); + double dist_p_p1 = p.distance_to_point (p1); + return (dist_p_p0 < dist_p_p1 ? dist_p_p0 : dist_p_p1) * (-(temp.n * Vector (p) - temp.c) < 0 ? -1 : 1); +} + + +inline double Segment::squared_distance_to_point (const Point &p) const { + if (p0 == p1) + return 0; + + // Check if z is between p0 and p1. + Line temp (p0, p1); + if (contains_in_span (p)) + return (temp.n * Vector (p) - temp.c) * (temp.n * Vector (p) - temp.c) / (temp.n * temp.n); + + double dist_p_p0 = p.squared_distance_to_point (p0); + double dist_p_p1 = p.squared_distance_to_point (p1); + return (dist_p_p0 < dist_p_p1 ? dist_p_p0 : dist_p_p1); +} + + +inline double Segment::max_distance_to_arc (const Arc &a) const { + double max_distance = fabs(a.distance_to_point(p0)) ; + return max_distance > fabs(a.distance_to_point(p1)) ? max_distance : fabs(a.distance_to_point(p1)) ; +} + + + +/* Arc */ + +inline bool Arc::operator == (const Arc &a) const { + return p0 == a.p0 && p1 == a.p1 && d == a.d; +} +inline bool Arc::operator != (const Arc &a) const { + return !(*this == a); +} + + +inline const SignedVector Arc::operator- (const Point &p) const { + + if (fabs(d) < 1e-5) { + Segment arc_segment (p0, p1); + return arc_segment - p; + } + if (wedge_contains_point (p)){ + Vector difference = (center () - p).normalized () * fabs (p.distance_to_point (center ()) - radius ()); + + return SignedVector (difference, ((p - center ()).len () < radius ()) ^ (d < 0)); + } + double d0 = p.squared_distance_to_point (p0); + double d1 = p.squared_distance_to_point (p1); + + Arc other_arc (p0, p1, (1.0 + d) / (1.0 - d)); /********************************* NOT Robust. But works? *****************/ + Vector normal = center () - (d0 < d1 ? p0 : p1) ; + + if (normal.len() == 0) + return SignedVector (Vector (0, 0), true); /************************************ Check sign of this S.D. *************/ + + return SignedVector (Line (normal.dx, normal.dy, normal * Vector ((d0 < d1 ? p0 : p1))) - p, !other_arc.wedge_contains_point(p)); +} + +inline const SignedVector operator- (const Point &p, const Arc &a) { + return -(a - p); +} + + + +inline double Arc::radius (void) const +{ + return fabs ((p1 - p0).len () / (2 * sin2atan (d))); +} + +inline const Point Arc::center (void) const +{ + return (p0.midpoint (p1)) + (p1 - p0).ortho () / (2 * tan2atan (d)); +} + +inline const Pair Arc::tangents (void) const +{ + Vector dp = (p1 - p0) * .5; + Vector pp = dp.ortho () * -sin2atan (d); + dp = dp * cos2atan (d); + return Pair (dp + pp, dp - pp); +} + + + +inline Bezier Arc::approximate_bezier (double *error) const +{ + Vector dp = p1 - p0; + Vector pp = dp.ortho (); + + if (error) + *error = dp.len () * pow (fabs (d), 5) / (54 * (1 + d*d)); + + dp *= ((1 - d*d) / 3); + pp *= (2 * d / 3); + + Point p0s = p0 + dp - pp; + Point p1s = p1 - dp - pp; + + return Bezier (p0, p0s, p1s, p1); +} + + +inline bool Arc::wedge_contains_point (const Point &p) const +{ + Pair t = tangents (); + if (fabs (d) <= 1) + return (p - p0) * t.first >= 0 && (p - p1) * t.second <= 0; + else + return (p - p0) * t.first >= 0 || (p - p1) * t.second <= 0; +} + + +/* Distance may not always be positive, but will be to an endpoint whenever necessary. */ +inline double Arc::distance_to_point (const Point &p) const { + if (fabs(d) < 1e-5) { + Segment arc_segment (p0, p1); + return arc_segment.distance_to_point (p); + } + + SignedVector difference = *this - p; + + if (wedge_contains_point (p) && fabs(d) > 1e-5) + return fabs (p.distance_to_point (center ()) - radius ()) * (difference.negative ? -1 : 1); + double d1 = p.squared_distance_to_point (p0); + double d2 = p.squared_distance_to_point (p1); + return (d1 < d2 ? sqrt(d1) : sqrt(d2)) * (difference.negative ? -1 : 1); +} + +/* Distance will be to an endpoint whenever necessary. */ +inline double Arc::squared_distance_to_point (const Point &p) const { + if (fabs(d) < 1e-5) { + Segment arc_segment (p0, p1); + return arc_segment.squared_distance_to_point (p); + } + + //SignedVector difference = *this - p; + + if (wedge_contains_point (p) && fabs(d) > 1e-5) { + double answer = p.distance_to_point (center ()) - radius (); + return answer * answer; + } + double d1 = p.squared_distance_to_point (p0); + double d2 = p.squared_distance_to_point (p1); + return (d1 < d2 ? d1 : d2); +} + +inline double Arc::extended_dist (const Point &p) const { + Point m = p0.lerp (.5, p1); + Vector dp = p1 - p0; + Vector pp = dp.ortho (); + float d2 = tan2atan (d); + if ((p - m) * (p1 - m) < 0) + return (p - p0) * (pp + dp * d2).normalized (); + else + return (p - p1) * (pp - dp * d2).normalized (); +} + +inline void Arc::extents (glyphy_extents_t &extents) const { + glyphy_extents_clear (&extents); + glyphy_extents_add (&extents, &p0); + glyphy_extents_add (&extents, &p1); + Point c = center (); + double r = radius (); + Point p[4] = {c + r * Vector (-1, 0), + c + r * Vector (+1, 0), + c + r * Vector ( 0, -1), + c + r * Vector ( 0, +1)}; + for (unsigned int i = 0; i < 4; i++) + if (wedge_contains_point (p[i])) + glyphy_extents_add (&extents, &p[i]); +} + + +/* Bezier */ + +inline const Point Bezier::point (const double &t) const { + Point p01 = p0.lerp (t, p1); + Point p12 = p1.lerp (t, p2); + Point p23 = p2.lerp (t, p3); + Point p012 = p01.lerp (t, p12); + Point p123 = p12.lerp (t, p23); + Point p0123 = p012.lerp (t, p123); + return p0123; +} + +inline const Point Bezier::midpoint (void) const +{ + Point p01 = p0.midpoint (p1); + Point p12 = p1.midpoint (p2); + Point p23 = p2.midpoint (p3); + Point p012 = p01.midpoint (p12); + Point p123 = p12.midpoint (p23); + Point p0123 = p012.midpoint (p123); + return p0123; +} + +inline const Vector Bezier::tangent (const double &t) const +{ + double t_2_0 = t * t; + double t_0_2 = (1 - t) * (1 - t); + + double _1__4t_1_0_3t_2_0 = 1 - 4 * t + 3 * t_2_0; + double _2t_1_0_3t_2_0 = 2 * t - 3 * t_2_0; + + return Vector (-3 * p0.x * t_0_2 + +3 * p1.x * _1__4t_1_0_3t_2_0 + +3 * p2.x * _2t_1_0_3t_2_0 + +3 * p3.x * t_2_0, + -3 * p0.y * t_0_2 + +3 * p1.y * _1__4t_1_0_3t_2_0 + +3 * p2.y * _2t_1_0_3t_2_0 + +3 * p3.y * t_2_0); +} + +inline const Vector Bezier::d_tangent (const double &t) const { + return Vector (6 * ((-p0.x + 3*p1.x - 3*p2.x + p3.x) * t + (p0.x - 2*p1.x + p2.x)), + 6 * ((-p0.y + 3*p1.y - 3*p2.y + p3.y) * t + (p0.y - 2*p1.y + p2.y))); +} + +inline double Bezier::curvature (const double &t) const { + Vector dpp = tangent (t).ortho (); + Vector ddp = d_tangent (t); + /* normal vector len squared */ + double len = dpp.len (); + double curvature = (dpp * ddp) / (len * len * len); + return curvature; +} + +inline const Pair Bezier::split (const double &t) const { + Point p01 = p0.lerp (t, p1); + Point p12 = p1.lerp (t, p2); + Point p23 = p2.lerp (t, p3); + Point p012 = p01.lerp (t, p12); + Point p123 = p12.lerp (t, p23); + Point p0123 = p012.lerp (t, p123); + return Pair (Bezier (p0, p01, p012, p0123), + Bezier (p0123, p123, p23, p3)); +} + +inline const Pair Bezier::halve (void) const +{ + Point p01 = p0.midpoint (p1); + Point p12 = p1.midpoint (p2); + Point p23 = p2.midpoint (p3); + Point p012 = p01.midpoint (p12); + Point p123 = p12.midpoint (p23); + Point p0123 = p012.midpoint (p123); + return Pair (Bezier (p0, p01, p012, p0123), + Bezier (p0123, p123, p23, p3)); +} + +inline const Bezier Bezier::segment (const double &t0, const double &t1) const +{ + Point p01 = p0.lerp (t0, p1); + Point p12 = p1.lerp (t0, p2); + Point p23 = p2.lerp (t0, p3); + Point p012 = p01.lerp (t0, p12); + Point p123 = p12.lerp (t0, p23); + Point p0123 = p012.lerp (t0, p123); + + Point q01 = p0.lerp (t1, p1); + Point q12 = p1.lerp (t1, p2); + Point q23 = p2.lerp (t1, p3); + Point q012 = q01.lerp (t1, q12); + Point q123 = q12.lerp (t1, q23); + Point q0123 = q012.lerp (t1, q123); + + return Bezier (p0123, + p0123 + (p123 - p0123) * ((t1 - t0) / (1 - t0)), + q0123 + (q012 - q0123) * ((t1 - t0) / t1), + q0123); +} + +} /* namespace Geometry */ +} /* namespace GLyphy */ + +#endif /* GLYPHY_GEOMETRY_HH */ diff --git a/text/dali/internal/glyphy/glyphy-outline.cc b/text/dali/internal/glyphy/glyphy-outline.cc new file mode 100644 index 0000000..988cc4d --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-outline.cc @@ -0,0 +1,368 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glyphy-common.hh" +#include "glyphy-geometry.hh" + +using namespace GLyphy::Geometry; + + +void +glyphy_outline_reverse (glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints) +{ + if (!num_endpoints) + return; + + // Shift the d's first + double d0 = endpoints[0].d; + for (unsigned int i = 0; i < num_endpoints - 1; i++) + endpoints[i].d = endpoints[i + 1].d == GLYPHY_INFINITY ? GLYPHY_INFINITY : -endpoints[i + 1].d; + endpoints[num_endpoints - 1].d = d0; + + // Reverse + for (unsigned int i = 0, j = num_endpoints - 1; i < j; i++, j--) { + glyphy_arc_endpoint_t t = endpoints[i]; + endpoints[i] = endpoints[j]; + endpoints[j] = t; + } +} + + +static bool +winding (const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints) +{ + /* + * Algorithm: + * + * - Find the lowest-x part of the contour, + * - If the point is an endpoint: + * o compare the angle of the incoming and outgoing edges of that point + * to find out whether it's CW or CCW, + * - Otherwise, compare the y of the two endpoints of the arc with lowest-x point. + * + * Note: + * + * We can use a simpler algorithm here: Act as if arcs are lines, then use the + * triangle method to calculate the signed area of the contour and get the sign. + * It should work for all cases we care about. The only case failing would be + * that of two endpoints and two arcs. But we can even special-case that. + */ + + unsigned int corner = 1; + for (unsigned int i = 2; i < num_endpoints; i++) + if (endpoints[i].p.x < endpoints[corner].p.x || + (endpoints[i].p.x == endpoints[corner].p.x && + endpoints[i].p.y < endpoints[corner].p.y)) + corner = i; + + double min_x = endpoints[corner].p.x; + int winner = -1; + Point p0 (0, 0); + for (unsigned int i = 0; i < num_endpoints; i++) { + const glyphy_arc_endpoint_t &endpoint = endpoints[i]; + if (endpoint.d == GLYPHY_INFINITY || endpoint.d == 0 /* arcs only, not lines */) { + p0 = endpoint.p; + continue; + } + Arc arc (p0, endpoint.p, endpoint.d); + p0 = endpoint.p; + + Point c = arc.center (); + double r = arc.radius (); + if (c.x - r < min_x && arc.wedge_contains_point (c - Vector (r, 0))) { + min_x = c.x - r; + winner = i; + } + } + + if (winner == -1) + { + // Corner is lowest-x. Find the tangents of the two arcs connected to the + // corner and compare the tangent angles to get contour direction. + const glyphy_arc_endpoint_t ethis = endpoints[corner]; + const glyphy_arc_endpoint_t eprev = endpoints[corner - 1]; + const glyphy_arc_endpoint_t enext = endpoints[corner < num_endpoints - 1 ? corner + 1 : 1]; + double in = (-Arc (eprev.p, ethis.p, ethis.d).tangents ().second).angle (); + double out = (+Arc (ethis.p, enext.p, enext.d).tangents ().first ).angle (); + return out > in; + } + else + { + // Easy. + return endpoints[winner].d < 0; + } + + return false; +} + + +static int +categorize (double v, double ref) +{ + return v < ref - GLYPHY_EPSILON ? -1 : v > ref + GLYPHY_EPSILON ? +1 : 0; +} + +static bool +is_zero (double v) +{ + return fabs (v) < GLYPHY_EPSILON; +} + +static bool +even_odd (const glyphy_arc_endpoint_t *c_endpoints, + unsigned int num_c_endpoints, + const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints) +{ + /* + * Algorithm: + * + * - For a point on the contour, draw a halfline in a direction + * (eg. decreasing x) to infinity, + * - Count how many times it crosses all other contours, + * - Pay special attention to points falling exactly on the halfline, + * specifically, they count as +.5 or -.5, depending the direction + * of crossing. + * + * All this counting is extremely tricky: + * + * - Floating point equality cannot be relied on here, + * - Lots of arc analysis needed, + * - Without having a point that we know falls /inside/ the contour, + * there are legitimate cases that we simply cannot handle using + * this algorithm. For example, imagine the following glyph shape: + * + * +---------+ + * | +-----+ | + * | \ / | + * | \ / | + * +----o----+ + * + * If the glyph is defined as two outlines, and when analysing the + * inner outline we happen to pick the point denoted by 'o' for + * analysis, there simply is no way to differentiate this case from + * the following case: + * + * +---------+ + * | | + * | | + * | | + * +----o----+ + * / \ + * / \ + * +-----+ + * + * However, in one, the triangle should be filled in, and in the other + * filled out. + * + * One way to work around this may be to do the analysis for all endpoints + * on the outline and take majority. But even that can fail in more + * extreme yet legitimate cases, such as this one: + * + * +--+--+ + * | / \ | + * |/ \| + * + + + * |\ /| + * | \ / | + * +--o--+ + * + * The only correct algorithm I can think of requires a point that falls + * fully inside the outline. While we can try finding such a point (not + * dissimilar to the winding algorithm), it's beyond what I'm willing to + * implement right now. + */ + + const Point p = c_endpoints[0].p; + + double count = 0; + Point p0 (0, 0); + for (unsigned int i = 0; i < num_endpoints; i++) { + const glyphy_arc_endpoint_t &endpoint = endpoints[i]; + if (endpoint.d == GLYPHY_INFINITY) { + p0 = endpoint.p; + continue; + } + Arc arc (p0, endpoint.p, endpoint.d); + p0 = endpoint.p; + + /* + * Skip our own contour + */ + if (&endpoint >= c_endpoints && &endpoint < c_endpoints + num_c_endpoints) + continue; + + /* End-point y's compared to the ref point; lt, eq, or gt */ + unsigned s0 = categorize (arc.p0.y, p.y); + unsigned s1 = categorize (arc.p1.y, p.y); + + if (is_zero (arc.d)) + { + /* Line */ + + if (!s0 || !s1) + { + /* + * Add +.5 / -.5 for each endpoint on the halfline, depending on + * crossing direction. + */ + Pair t = arc.tangents (); + if (!s0 && arc.p0.x < p.x + GLYPHY_EPSILON) + count += .5 * categorize (t.first.dy, 0); + if (!s1 && arc.p1.x < p.x + GLYPHY_EPSILON) + count += .5 * categorize (t.second.dy, 0); + continue; + } + + if (s0 == s1) + continue; // Segment fully above or below the halfline + + // Find x pos that the line segment would intersect the half-line. + double x = arc.p0.x + (arc.p1.x - arc.p0.x) * ((p.y - arc.p0.y) / (arc.p1.y - arc.p0.y)); + + if (x >= p.x - GLYPHY_EPSILON) + continue; // Does not intersect halfline + + count++; // Add one for full crossing + continue; + } + else + { + /* Arc */ + + if (!s0 || !s1) + { + /* + * Add +.5 / -.5 for each endpoint on the halfline, depending on + * crossing direction. + */ + Pair t = arc.tangents (); + + /* Arc-specific logic: + * If the tangent has dy==0, use the other endpoint's + * y value to decide which way the arc will be heading. + */ + if (is_zero (t.first.dy)) + t.first.dy = +categorize (arc.p1.y, p.y); + if (is_zero (t.second.dy)) + t.second.dy = -categorize (arc.p0.y, p.y); + + if (!s0 && arc.p0.x < p.x + GLYPHY_EPSILON) + count += .5 * categorize (t.first.dy, 0); + if (!s1 && arc.p1.x < p.x + GLYPHY_EPSILON) + count += .5 * categorize (t.second.dy, 0); + } + + Point c = arc.center (); + double r = arc.radius (); + if (c.x - r >= p.x) + continue; // No chance + /* Solve for arc crossing line with y = p.y */ + double dy = p.y - c.y; + double x2 = r * r - dy * dy; + if (x2 <= GLYPHY_EPSILON) + continue; // Negative delta, no crossing + double dx = sqrt (x2); + /* There's two candidate points on the arc with the same y as the + * ref point. */ + Point pp[2] = { Point (c.x - dx, p.y), + Point (c.x + dx, p.y) }; + +#define POINTS_EQ(a,b) (is_zero (a.x - b.x) && is_zero (a.y - b.y)) + for (unsigned int i = 0; i < ARRAY_LENGTH (pp); i++) + { + /* Make sure we don't double-count endpoints that fall on the + * halfline as we already accounted for those above */ + if (!POINTS_EQ (pp[i], arc.p0) && !POINTS_EQ (pp[i], arc.p1) && + pp[i].x < p.x - GLYPHY_EPSILON && arc.wedge_contains_point (pp[i])) + count++; // Add one for full crossing + } +#undef POINTS_EQ + } + } + + return !(int (floor (count)) & 1); +} + +static bool +process_contour (glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + const glyphy_arc_endpoint_t *all_endpoints, + unsigned int num_all_endpoints, + bool inverse) +{ + /* + * Algorithm: + * + * - Find the winding direction and even-odd number, + * - If the two disagree, reverse the contour, inplace. + */ + + if (!num_endpoints) + return false; + + if (num_endpoints < 3) { + abort (); // Don't expect this + return false; // Need at least two arcs + } + if (Point (endpoints[0].p) != Point (endpoints[num_endpoints-1].p)) { + abort (); // Don't expect this + return false; // Need a closed contour + } + + if (inverse ^ + winding (endpoints, num_endpoints) ^ + even_odd (endpoints, num_endpoints, all_endpoints, num_all_endpoints)) + { + glyphy_outline_reverse (endpoints, num_endpoints); + return true; + } + + return false; +} + +/* Returns true if outline was modified */ +glyphy_bool_t +glyphy_outline_winding_from_even_odd (glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + glyphy_bool_t inverse) +{ + /* + * Algorithm: + * + * - Process one contour at a time. + */ + + unsigned int start = 0; + bool ret = false; + for (unsigned int i = 1; i < num_endpoints; i++) { + const glyphy_arc_endpoint_t &endpoint = endpoints[i]; + if (endpoint.d == GLYPHY_INFINITY) { + ret = ret | process_contour (endpoints + start, i - start, endpoints, num_endpoints, bool (inverse)); + start = i; + } + } + ret = ret | process_contour (endpoints + start, num_endpoints - start, endpoints, num_endpoints, bool (inverse)); + return ret; +} diff --git a/text/dali/internal/glyphy/glyphy-sdf.cc b/text/dali/internal/glyphy/glyphy-sdf.cc new file mode 100644 index 0000000..e0aa1af --- /dev/null +++ b/text/dali/internal/glyphy/glyphy-sdf.cc @@ -0,0 +1,85 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glyphy-common.hh" +#include "glyphy-geometry.hh" + +using namespace GLyphy::Geometry; + +double +glyphy_sdf_from_arc_list (const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + const glyphy_point_t *p, + glyphy_point_t *closest_p /* may be NULL; TBD not implemented yet */) +{ + Point c = *p; + Point p0 (0, 0); + Arc closest_arc (p0, p0, 0); + double min_dist = GLYPHY_INFINITY; + int side = 0; + for (unsigned int i = 0; i < num_endpoints; i++) { + const glyphy_arc_endpoint_t &endpoint = endpoints[i]; + if (endpoint.d == GLYPHY_INFINITY) { + p0 = endpoint.p; + continue; + } + Arc arc (p0, endpoint.p, endpoint.d); + p0 = endpoint.p; + + if (arc.wedge_contains_point (c)) { + double sdist = arc.distance_to_point (c); /* TODO This distance has the wrong sign. Fix */ + double udist = fabs (sdist) * (1 - GLYPHY_EPSILON); + if (udist <= min_dist) { + min_dist = udist; + side = sdist >= 0 ? -1 : +1; + } + } else { + double udist = std::min ((arc.p0 - c).len (), (arc.p1 - c).len ()); + if (udist < min_dist) { + min_dist = udist; + side = 0; /* unsure */ + closest_arc = arc; + } else if (side == 0 && udist == min_dist) { + /* If this new distance is the same as the current minimum, + * compare extended distances. Take the sign from the arc + * with larger extended distance. */ + double old_ext_dist = closest_arc.extended_dist (c); + double new_ext_dist = arc.extended_dist (c); + + double ext_dist = fabs (new_ext_dist) <= fabs (old_ext_dist) ? + old_ext_dist : new_ext_dist; + + /* For emboldening and stuff: */ + // min_dist = fabs (ext_dist); + side = ext_dist >= 0 ? +1 : -1; + } + } + } + + if (side == 0) { + // Technically speaking this should not happen, but it does. So try to fix it. + double ext_dist = closest_arc.extended_dist (c); + side = ext_dist >= 0 ? +1 : -1; + } + + return side * min_dist; +} diff --git a/text/dali/internal/glyphy/glyphy.h b/text/dali/internal/glyphy/glyphy.h new file mode 100644 index 0000000..087587b --- /dev/null +++ b/text/dali/internal/glyphy/glyphy.h @@ -0,0 +1,218 @@ +/* + * Copyright 2012 Google, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Google Author(s): Behdad Esfahbod, Maysum Panju + */ + +#ifndef GLYPHY_H +#define GLYPHY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int glyphy_bool_t; + +typedef struct { + double x; + double y; +} glyphy_point_t; + +/* + * Geometry extents + */ + +typedef struct { + double min_x; + double min_y; + double max_x; + double max_y; +} glyphy_extents_t; + +void +glyphy_extents_clear (glyphy_extents_t *extents); + +glyphy_bool_t +glyphy_extents_is_empty (const glyphy_extents_t *extents); + +void +glyphy_extents_add (glyphy_extents_t *extents, + const glyphy_point_t *p); + +void +glyphy_extents_extend (glyphy_extents_t *extents, + const glyphy_extents_t *other); + +glyphy_bool_t +glyphy_extents_includes (const glyphy_extents_t *extents, + const glyphy_point_t *p); + +void +glyphy_extents_scale (glyphy_extents_t *extents, + double x_scale, + double y_scale); + +/* + * Circular arcs + */ + +typedef struct { + glyphy_point_t p0; + glyphy_point_t p1; + double d; +} glyphy_arc_t; + +/* + * Approximate outlines with multiple arcs + */ + +typedef struct { + glyphy_point_t p; + double d; +} glyphy_arc_endpoint_t; + +typedef glyphy_bool_t (*glyphy_arc_endpoint_accumulator_callback_t) (glyphy_arc_endpoint_t *endpoint, + void *user_data); + +typedef struct glyphy_arc_accumulator_t glyphy_arc_accumulator_t; + +glyphy_arc_accumulator_t * +glyphy_arc_accumulator_create (void); + +void +glyphy_arc_accumulator_destroy (glyphy_arc_accumulator_t *acc); + +void +glyphy_arc_accumulator_reset (glyphy_arc_accumulator_t *acc); + +/* Configure accumulator */ + +void +glyphy_arc_accumulator_set_tolerance (glyphy_arc_accumulator_t *acc, + double tolerance); + +double +glyphy_arc_accumulator_get_tolerance (glyphy_arc_accumulator_t *acc); + +void +glyphy_arc_accumulator_set_callback (glyphy_arc_accumulator_t *acc, + glyphy_arc_endpoint_accumulator_callback_t callback, + void *user_data); + +void +glyphy_arc_accumulator_get_callback (glyphy_arc_accumulator_t *acc, + glyphy_arc_endpoint_accumulator_callback_t *callback, + void **user_data); + +/* Accumulation results */ + +double +glyphy_arc_accumulator_get_error (glyphy_arc_accumulator_t *acc); + +glyphy_bool_t +glyphy_arc_accumulator_successful (glyphy_arc_accumulator_t *acc); + + +/* Accumulate */ + +void +glyphy_arc_accumulator_move_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p0); + +void +glyphy_arc_accumulator_line_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1); + +void +glyphy_arc_accumulator_conic_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1, + const glyphy_point_t *p2); + +void +glyphy_arc_accumulator_cubic_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1, + const glyphy_point_t *p2, + const glyphy_point_t *p3); + +void +glyphy_arc_accumulator_arc_to (glyphy_arc_accumulator_t *acc, + const glyphy_point_t *p1, + double d); + +void +glyphy_arc_accumulator_close_path (glyphy_arc_accumulator_t *acc); + +void +glyphy_arc_list_extents (const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + glyphy_extents_t *extents); + +/* + * Modify outlines for proper consumption + */ + +void +glyphy_outline_reverse (glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints); + +/* Returns true if outline was modified */ +glyphy_bool_t +glyphy_outline_winding_from_even_odd (glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + glyphy_bool_t inverse); + +/* + * Encode an arc outline into binary blob for fast SDF calculation + */ + +typedef Dali::TextAbstraction::VectorBlob glyphy_rgba_t; + +glyphy_bool_t +glyphy_arc_list_encode_blob (const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + glyphy_rgba_t *blob, + unsigned int blob_size, + double faraway, + double avg_fetch_desired, + double *avg_fetch_achieved, + unsigned int *output_len, + unsigned int *nominal_width, /* 6bit */ + unsigned int *nominal_height, /* 6bit */ + glyphy_extents_t *extents); + +/* + * Calculate signed-distance-field from (encoded) arc list + */ + +double +glyphy_sdf_from_arc_list (const glyphy_arc_endpoint_t *endpoints, + unsigned int num_endpoints, + const glyphy_point_t *p, + glyphy_point_t *closest_p /* may be NULL; TBD not implemented yet */); + +double +glyphy_sdf_from_blob (const glyphy_rgba_t *blob, + unsigned int nominal_width, + unsigned int nominal_height, + const glyphy_point_t *p, + glyphy_point_t *closest_p /* may be NULL; TBD not implemented yet */); + +#ifdef __cplusplus +} +#endif + +#endif /* GLYPHY_H */ diff --git a/text/dali/internal/glyphy/vector-font-cache.cpp b/text/dali/internal/glyphy/vector-font-cache.cpp new file mode 100644 index 0000000..972327a --- /dev/null +++ b/text/dali/internal/glyphy/vector-font-cache.cpp @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// EXTERNAL INCLUDES +#include +#include + +// INTERNAL INCLUDES +#include "../glyphy/glyphy.h" +#include "../glyphy/glyphy-freetype.h" + +using namespace std; + +namespace +{ + +const unsigned int INITIAL_GLYPH_CAPACITY = 50; +const double MIN_FONT_SIZE = 10; + +static glyphy_bool_t +accumulate_endpoint( glyphy_arc_endpoint_t* endpoint, + vector* endpoints ) +{ + endpoints->push_back( *endpoint ); + return true; +} + +} // unnamed namespace + +namespace Dali +{ + +namespace TextAbstraction +{ + +namespace Internal +{ + +typedef vector BlobArray; + +struct VectorGlyph +{ + /** + * @brief Create a vector-based glyph. + */ + static VectorGlyph* New( FT_Face face, + FontId fontId, + GlyphIndex index, + glyphy_arc_accumulator_t* accumulator ) + { + VectorGlyph* newGlyph = new VectorGlyph(); + newGlyph->blobData.resize( 1024 * 16 ); + + if( FT_Err_Ok != FT_Load_Glyph( face, + index, + FT_LOAD_NO_BITMAP | + FT_LOAD_NO_HINTING | + FT_LOAD_NO_AUTOHINT | + FT_LOAD_NO_SCALE | + FT_LOAD_LINEAR_DESIGN | + FT_LOAD_IGNORE_TRANSFORM)) + { + DALI_LOG_ERROR( "FT_Load_Glyph failed\n" ); + delete newGlyph; + return NULL; + } + + const double upem = static_cast( face->units_per_EM ); + const double tolerance = upem * 1.0f/2048.0f; + + glyphy_arc_accumulator_reset( accumulator); + glyphy_arc_accumulator_set_tolerance( accumulator, tolerance ); + + vector endpoints; + glyphy_arc_accumulator_set_callback( accumulator, + reinterpret_cast( accumulate_endpoint ), + &endpoints ); + + if( FT_Err_Ok != glyphy_freetype_outline_decompose( &face->glyph->outline, accumulator ) ) + { + DALI_LOG_ERROR( "glyphy_freetype_outline_decompose failed\n" ); + delete newGlyph; + return NULL; + } + + DALI_ASSERT_DEBUG( glyphy_arc_accumulator_get_error(accumulator) <= tolerance && "glyphy_arc_accumulator_get_error > tolerance" ); + + if( endpoints.size() ) + { + glyphy_outline_winding_from_even_odd( &endpoints[0], endpoints.size (), false ); + } + + unsigned int blobLength( 0 ); + double averageFetchAchieved( 0.0 ); + if (!glyphy_arc_list_encode_blob( endpoints.size() ? &endpoints[0] : NULL, + endpoints.size(), + &newGlyph->blobData[0], + newGlyph->blobData.capacity(), + upem / ( MIN_FONT_SIZE * M_SQRT2 ), + 4, + &averageFetchAchieved, + &blobLength, + &newGlyph->nominalWidth, + &newGlyph->nominalHeight, + &newGlyph->extents ) ) + { + DALI_LOG_ERROR( "glyphy_arc_list_encode_blob failed\n" ); + delete newGlyph; + return NULL; + } + newGlyph->blobData.resize( blobLength ); + + glyphy_extents_scale( &newGlyph->extents, 1.0/upem, 1.0/upem ); + + newGlyph->glyphInfo.fontId = fontId; + newGlyph->glyphInfo.index = index; + + if( glyphy_extents_is_empty( &newGlyph->extents ) ) + { + newGlyph->glyphInfo.width = 0.0f; + newGlyph->glyphInfo.height = 0.0f; + + newGlyph->glyphInfo.xBearing = 0.0f; + newGlyph->glyphInfo.yBearing = 0.0f; + } + else + { + newGlyph->glyphInfo.width = (newGlyph->extents.max_x - newGlyph->extents.min_x); + newGlyph->glyphInfo.height = (newGlyph->extents.max_y - newGlyph->extents.min_y); + + newGlyph->glyphInfo.xBearing = newGlyph->extents.min_x; + newGlyph->glyphInfo.yBearing = newGlyph->glyphInfo.height + (newGlyph->extents.min_y); + } + + newGlyph->glyphInfo.advance = face->glyph->metrics.horiAdvance / upem; + newGlyph->glyphInfo.scaleFactor = 0.0f; + + return newGlyph; + } + + VectorGlyph() + : advance( 0.0 ), + nominalWidth( 0 ), + nominalHeight( 0 ), + glyphInfo(), + blobData() + { + glyphy_extents_clear( &extents ); + } + + glyphy_extents_t extents; + double advance; + unsigned int nominalWidth; + unsigned int nominalHeight; + GlyphInfo glyphInfo; + BlobArray blobData; +}; + +typedef vector GlyphCache; + +struct VectorFont +{ + VectorFont( FT_Face face ) + : mFace( face ), + mGlyphCache() + { + mGlyphCache.reserve( INITIAL_GLYPH_CAPACITY ); + } + + FT_Face mFace; + GlyphCache mGlyphCache; +}; + +struct VectorFontCache::Impl +{ + Impl( FT_Library freeTypeLibrary ) + : mFreeTypeLibrary( freeTypeLibrary ), + mIdLookup(), + mVectorFonts(), + mAccumulator( NULL ) + { + mAccumulator = glyphy_arc_accumulator_create(); + } + + ~Impl() + { + glyphy_arc_accumulator_destroy( mAccumulator ); + } + + FT_Library mFreeTypeLibrary; ///< A handle to a FreeType library instance. + + vector mIdLookup; + + vector mVectorFonts; + + glyphy_arc_accumulator_t* mAccumulator; +}; + +VectorFontCache::VectorFontCache( FT_Library freeTypeLibrary ) +: mImpl( NULL ) +{ + mImpl = new Impl( freeTypeLibrary ); +} + +VectorFontCache::~VectorFontCache() +{ + delete mImpl; +} + +FontId VectorFontCache::GetFontId( const std::string& url ) +{ + FontId id( 0 ); + + if( mImpl ) + { + if( ! FindFont( url, id ) ) + { + id = CreateFont( url ); + } + } + + return id; +} + +void VectorFontCache::GetGlyphMetrics( FontId vectorFontId, GlyphInfo& glyphInfo ) +{ + if( mImpl ) + { + if( vectorFontId > 0 && + vectorFontId-1 < mImpl->mVectorFonts.size() ) + { + VectorFont* font = mImpl->mVectorFonts[ vectorFontId-1 ]; + GlyphCache& cache = font->mGlyphCache; + + bool foundGlyph( false ); + unsigned int foundIndex( 0 ); + for( unsigned int i=0; iglyphInfo.index == glyphInfo.index ) + { + foundIndex = i; + foundGlyph = true; + break; + } + } + + if( foundGlyph ) + { + VectorGlyph* glyph = cache[foundIndex]; + // Note - this clobbers the original fontId, but helps avoid duplicating identical blobs + // e.g. if when the same font family is requested in different point-sizes + glyphInfo = glyph->glyphInfo; + } + else + { + VectorGlyph* newGlyph = VectorGlyph::New( font->mFace, + glyphInfo.fontId, + glyphInfo.index, + mImpl->mAccumulator ); + + if( newGlyph ) + { + glyphInfo = newGlyph->glyphInfo; + + cache.push_back( newGlyph ); + } + } + } + } +} + +void VectorFontCache::GetVectorBlob( FontId vectorFontId, + FontId fontId, + GlyphIndex glyphIndex, + VectorBlob*& blob, + unsigned int& blobLength, + unsigned int& nominalWidth, + unsigned int& nominalHeight ) +{ + if( mImpl ) + { + if( vectorFontId > 0 && + vectorFontId-1 < mImpl->mVectorFonts.size() ) + { + VectorFont* font = mImpl->mVectorFonts[ vectorFontId-1 ]; + GlyphCache& cache = font->mGlyphCache; + + bool foundGlyph( false ); + unsigned int foundIndex( 0 ); + for( unsigned int i=0; iglyphInfo.index == glyphIndex ) + { + foundIndex = i; + foundGlyph = true; + break; + } + } + + if( foundGlyph ) + { + VectorGlyph* glyph = cache[foundIndex]; + + blob = &glyph->blobData[0]; + blobLength = glyph->blobData.size(); + nominalWidth = glyph->nominalWidth; + nominalHeight = glyph->nominalHeight; + } + else + { + VectorGlyph* newGlyph = VectorGlyph::New( font->mFace, fontId, glyphIndex, mImpl->mAccumulator ); + + if( newGlyph ) + { + blob = &newGlyph->blobData[0]; + blobLength = newGlyph->blobData.size(); + nominalWidth = newGlyph->nominalWidth; + nominalHeight = newGlyph->nominalHeight; + + cache.push_back( newGlyph ); + } + } + } + } +} + +bool VectorFontCache::FindFont( const string& url, FontId& vectorFontId ) const +{ + vectorFontId = 0u; + + const vector& idLookup = mImpl->mIdLookup; + + for( unsigned int i=0; imFreeTypeLibrary, + url.c_str(), + 0, + &face ); + + if( FT_Err_Ok == error ) + { + mImpl->mIdLookup.push_back( url ); + id = mImpl->mIdLookup.size(); + + VectorFont* newFont = new VectorFont( face ); + mImpl->mVectorFonts.push_back( newFont ); + + DALI_ASSERT_DEBUG( mImpl->mIdLookup.size() == mImpl->mVectorFonts.size() ); + } + + return id; +} + +} // namespace Internal + +} // namespace TextAbstraction + +} // namespace Dali diff --git a/text/dali/internal/glyphy/vector-font-cache.h b/text/dali/internal/glyphy/vector-font-cache.h new file mode 100644 index 0000000..91fa2e4 --- /dev/null +++ b/text/dali/internal/glyphy/vector-font-cache.h @@ -0,0 +1,125 @@ +#ifndef __DALI_INTERNAL_TEXT_ABSTRACTION_VECTOR_FONT_CACHE_H__ +#define __DALI_INTERNAL_TEXT_ABSTRACTION_VECTOR_FONT_CACHE_H__ + +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include + +// INTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace TextAbstraction +{ + +namespace Internal +{ + +/** + * A cache of vector-based font data. + */ +class VectorFontCache +{ +public: + + /** + * Constructor + */ + VectorFontCache( FT_Library freeTypeLibrary ); + + /** + * Destructor + */ + ~VectorFontCache(); + + /** + * @brief Get the font ID for a vector-based font. + * + * @param[in] url The path to the font file. + * @return A valid font ID, or zero if the font does not exist. + */ + FontId GetFontId( const std::string& url ); + + /** + * @brief Get the unscaled metrics for a glyph. + * + * @param[in] vectorFontId The font ID for a vector-based font. + * @param[in,out] array A glyph-info structure with initialized FontId & GlyphIndex value. + * On return the size, bearing and advance values will be set. + */ + void GetGlyphMetrics( FontId vectorFontId, GlyphInfo& glyph_info ); + + /** + * @brief Get the vector representation of a glyph. + * + * @param[in] vectorFontId The font ID for a vector-based font. + * @param[in] fontId The equivalent font ID. + * @param[in] glyphIndex The index of a glyph within the specified font. + * @param[out] blob A blob of data; this is owned by VectorFontCache and should be copied by the caller of GetVectorBlob(). + * @param[out] blobLength The length of the blob data, or zero if the blob creation failed. + * @param[out] nominalWidth The width of the blob. + * @param[out] nominalHeight The height of the blob. + */ + void GetVectorBlob( FontId vectorFontId, FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight ); + +private: + + /** + * @brief Get the font ID for a vector-based font. + * + * @param[in] url The path to the font file. + * @param[out] fontId A valid font ID, or zero if the font does not exist. + * @return True if the font was found. + */ + bool FindFont( const std::string& url, FontId& fontId ) const; + + /** + * @brief Add a new vector-based font to the cache. + * + * @param[in] url The path to the font file. + * @return A valid font ID, or zero if the font does not exist. + */ + FontId CreateFont( const std::string& url ); + + // Undefined copy constructor. + VectorFontCache( const VectorFontCache& ); + + // Undefined assignment constructor. + VectorFontCache& operator=( VectorFontCache& ); + +private: + + struct Impl; + Impl* mImpl; +}; + +} // namespace Internal + +} // namespace TextAbstraction + +} // namespace Dali + +#endif // __DALI_INTERNAL_TEXT_ABSTRACTION_VECTOR_FONT_CACHE_H__ diff --git a/text/dali/internal/text-abstraction/font-client-impl.cpp b/text/dali/internal/text-abstraction/font-client-impl.cpp index 2521dd6..dbf4076 100644 --- a/text/dali/internal/text-abstraction/font-client-impl.cpp +++ b/text/dali/internal/text-abstraction/font-client-impl.cpp @@ -203,11 +203,11 @@ GlyphIndex FontClient::GetGlyphIndex( FontId fontId, Character charcode ) return mPlugin->GetGlyphIndex( fontId, charcode ); } -bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal, int desiredFixedSize ) +bool FontClient::GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal, int desiredFixedSize ) { CreatePlugin(); - return mPlugin->GetGlyphMetrics( array, size, horizontal, desiredFixedSize ); + return mPlugin->GetGlyphMetrics( array, size, type, horizontal, desiredFixedSize ); } BufferImage FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex ) @@ -217,6 +217,13 @@ BufferImage FontClient::CreateBitmap( FontId fontId, GlyphIndex glyphIndex ) return mPlugin->CreateBitmap( fontId, glyphIndex ); } +void FontClient::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight ) +{ + CreatePlugin(); + + return mPlugin->CreateVectorBlob( fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); +} + const GlyphInfo& FontClient::GetEllipsisGlyph( PointSize26Dot6 pointSize ) { CreatePlugin(); diff --git a/text/dali/internal/text-abstraction/font-client-impl.h b/text/dali/internal/text-abstraction/font-client-impl.h index 74edd11..aea274c 100644 --- a/text/dali/internal/text-abstraction/font-client-impl.h +++ b/text/dali/internal/text-abstraction/font-client-impl.h @@ -151,7 +151,7 @@ public: /** * @copydoc Dali::FontClient::GetGlyphMetrics() */ - bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal, int desiredFixedSize ); + bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal, int desiredFixedSize ); /** * @copydoc Dali::FontClient::CreateBitmap() @@ -159,6 +159,11 @@ public: BufferImage CreateBitmap( FontId fontId, GlyphIndex glyphIndex ); /** + * @copydoc Dali::FontClient::CreateVectorBlob() + */ + void CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight ); + + /** * @copydoc Dali::FontClient::GetEllipsisGlyph() */ const GlyphInfo& GetEllipsisGlyph( PointSize26Dot6 pointSize ); diff --git a/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp b/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp index 41bd899..d1ea359 100644 --- a/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp +++ b/text/dali/internal/text-abstraction/font-client-plugin-impl.cpp @@ -169,6 +169,7 @@ FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace, mMetrics( metrics ), mFixedWidthPixels( 0.0f ), mFixedHeightPixels( 0.0f ), + mVectorFontId( 0 ), mIsFixedSizeBitmap( false ) { } @@ -187,6 +188,7 @@ FontClient::Plugin::CacheItem::CacheItem( FT_Face ftFace, mMetrics( metrics ), mFixedWidthPixels( fixedWidth ), mFixedHeightPixels( fixedHeight ), + mVectorFontId( 0 ), mIsFixedSizeBitmap( true ) { } @@ -203,6 +205,7 @@ FontClient::Plugin::Plugin( unsigned int horizontalDpi, mValidatedFontCache(), mFontDescriptionCache( 1u ), mFontIdCache(), + mVectorFontCache( NULL ), mEllipsisCache(), mDefaultFontDescriptionCached( false ) { @@ -211,6 +214,10 @@ FontClient::Plugin::Plugin( unsigned int horizontalDpi, { DALI_LOG_ERROR( "FreeType Init error: %d\n", error ); } + +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING + mVectorFontCache = new VectorFontCache( mFreeTypeLibrary ); +#endif } FontClient::Plugin::~Plugin() @@ -228,6 +235,10 @@ FontClient::Plugin::~Plugin() } } +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING + delete mVectorFontCache; +#endif + FT_Done_FreeType( mFreeTypeLibrary ); } @@ -707,9 +718,23 @@ GlyphIndex FontClient::Plugin::GetGlyphIndex( FontId fontId, bool FontClient::Plugin::GetGlyphMetrics( GlyphInfo* array, uint32_t size, + GlyphType type, bool horizontal, int desiredFixedSize ) { + if( VECTOR_GLYPH == type ) + { + return GetVectorMetrics( array, size, horizontal, desiredFixedSize ); + } + + return GetBitmapMetrics( array, size, horizontal, desiredFixedSize ); +} + +bool FontClient::Plugin::GetBitmapMetrics( GlyphInfo* array, + uint32_t size, + bool horizontal, + int desiredFixedSize ) +{ bool success( true ); for( unsigned int i=0; i 0 && + fontId-1 < mFontCache.size() ) + { + CacheItem& font = mFontCache[fontId-1]; + + if( ! font.mVectorFontId ) + { + font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath ); + } + + mVectorFontCache->GetGlyphMetrics( font.mVectorFontId, array[i] ); + + // Vector metrics are in EMs, convert to pixels + float scale = (static_cast(font.mPointSize)/64.0f) * mDpiVertical/72.0f; + array[i].width *= scale; + array[i].height *= scale; + array[i].xBearing *= scale; + array[i].yBearing *= scale; + array[i].advance *= scale; + } + else + { + success = false; + } + } + + return success; +#else + return false; +#endif +} + BufferImage FontClient::Plugin::CreateBitmap( FontId fontId, GlyphIndex glyphIndex ) { @@ -853,6 +922,27 @@ BufferImage FontClient::Plugin::CreateBitmap( FontId fontId, return bitmap; } +void FontClient::Plugin::CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight ) +{ + blob = NULL; + blobLength = 0; + +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING + if( fontId > 0 && + fontId-1 < mFontCache.size() ) + { + CacheItem& font = mFontCache[fontId-1]; + + if( ! font.mVectorFontId ) + { + font.mVectorFontId = mVectorFontCache->GetFontId( font.mPath ); + } + + mVectorFontCache->GetVectorBlob( font.mVectorFontId, fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight ); + } +#endif +} + const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 pointSize ) { // First look into the cache if there is an ellipsis glyph for the requested point size. @@ -885,7 +975,7 @@ const GlyphInfo& FontClient::Plugin::GetEllipsisGlyph( PointSize26Dot6 pointSize item.glyph.index = FT_Get_Char_Index( mFontCache[item.glyph.fontId-1].mFreeTypeFace, ELLIPSIS_CHARACTER ); - GetGlyphMetrics( &item.glyph, 1u, true, 0 ); + GetBitmapMetrics( &item.glyph, 1u, true, 0 ); return item.glyph; } diff --git a/text/dali/internal/text-abstraction/font-client-plugin-impl.h b/text/dali/internal/text-abstraction/font-client-plugin-impl.h index 65cd9e5..d43e350 100644 --- a/text/dali/internal/text-abstraction/font-client-plugin-impl.h +++ b/text/dali/internal/text-abstraction/font-client-plugin-impl.h @@ -23,6 +23,12 @@ #include #include +#ifdef ENABLE_VECTOR_BASED_TEXT_RENDERING +#include +#else +class VectorFontCache; +#endif + // EXTERNAL INCLUDES #include #include FT_FREETYPE_H @@ -114,6 +120,7 @@ struct FontClient::Plugin FontMetrics mMetrics; ///< The font metrics. FT_Short mFixedWidthPixels; ///< The height in pixels (fixed size bitmaps only) FT_Short mFixedHeightPixels; ///< The height in pixels (fixed size bitmaps only) + unsigned int mVectorFontId; ///< The ID of the equivalent vector-based font bool mIsFixedSizeBitmap; ///< Whether the font has fixed size bitmaps. }; @@ -247,7 +254,17 @@ struct FontClient::Plugin /** * @copydoc Dali::FontClient::GetGlyphMetrics() */ - bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, bool horizontal, int desiredFixedSize ); + bool GetGlyphMetrics( GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal, int desiredFixedSize ); + + /** + * Helper for GetGlyphMetrics when using bitmaps + */ + bool GetBitmapMetrics( GlyphInfo* array, uint32_t size, bool horizontal, int desiredFixedSize ); + + /** + * Helper for GetGlyphMetrics when using vectors + */ + bool GetVectorMetrics( GlyphInfo* array, uint32_t size, bool horizontal, int desiredFixedSize ); /** * @copydoc Dali::FontClient::CreateBitmap() @@ -255,6 +272,11 @@ struct FontClient::Plugin BufferImage CreateBitmap( FontId fontId, GlyphIndex glyphIndex ); /** + * @copydoc Dali::FontClient::CreateVectorBlob() + */ + void CreateVectorBlob( FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight ); + + /** * @copydoc Dali::FontClient::GetEllipsisGlyph() */ const GlyphInfo& GetEllipsisGlyph( PointSize26Dot6 pointSize ); @@ -437,6 +459,8 @@ private: FontList mFontDescriptionCache; ///< Caches font descriptions for the validated font. std::vector mFontIdCache; ///< Caches font ids for the pairs of font point size and the index to the vector with font descriptions of the validated fonts. + VectorFontCache* mVectorFontCache; ///< Separate cache for vector data blobs etc. + Vector mEllipsisCache; ///< Caches ellipsis glyphs for a particular point size. bool mDefaultFontDescriptionCached : 1; ///< Whether the default font is cached or not