Skip to content

Commit 39103d9

Browse files
authored
[Impeller] libImpeller: Implement APIs for fetching glyph and line metrics. (#165701)
This patch adds support in the typography subsystem of the public Impeller API such that users can implement text editing functionality. * Line metrics for a fully laid out paragraph can be retrieved. The metrics contain information about offsets into the original code unit buffer used to create the paragraph. These offsets can be used to implement functionality that edits whole lines. * Glyph information for a specific code unit offset, as well as a coordinate offset relative to the paragraph origin, can be obtained. This information can be used place decorations (like carets), select words surrounding the caret (a hit-test), and edit the source buffer to re-layout the paragraph. * Word boundaries (as specified in Unicode Standard Annex 29) can be retrieved to select and modify paragraph subsets by character, word, and line at caret. Like in Flutter, the code unit buffers are assumed to be arrays of UTF-16 bytes. I'd have preferred this to be UTF-8 because the initial paragraph construction is using UTF-8 bytes. Also, Skia internally seems to work with UTF-8 too but the interfaces are exposed using UTF-16 (presumably for users like Flutter that work with Dart strings that are UTF-16). Exposing additional APIs in txt::Paragraph to back this out seemed onerous. Instead, a UTF-16 assumption for all APIs that retrieve metrics is made (and documented). It stands to reason that paragraphs should be constructable using UTF-16 buffers in the public API too. I'll add that in a subsequent patch as that has little to do with metrics. Fixes #165509
1 parent 9e829cb commit 39103d9

File tree

13 files changed

+1291
-144
lines changed

13 files changed

+1291
-144
lines changed

engine/src/flutter/ci/licenses_golden/licenses_flutter

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51763,13 +51763,17 @@ ORIGIN: ../../../flutter/impeller/toolkit/interop/example_mtl.m + ../../../flutt
5176351763
ORIGIN: ../../../flutter/impeller/toolkit/interop/example_vk.c + ../../../flutter/LICENSE
5176451764
ORIGIN: ../../../flutter/impeller/toolkit/interop/formats.cc + ../../../flutter/LICENSE
5176551765
ORIGIN: ../../../flutter/impeller/toolkit/interop/formats.h + ../../../flutter/LICENSE
51766+
ORIGIN: ../../../flutter/impeller/toolkit/interop/glyph_info.cc + ../../../flutter/LICENSE
51767+
ORIGIN: ../../../flutter/impeller/toolkit/interop/glyph_info.h + ../../../flutter/LICENSE
5176651768
ORIGIN: ../../../flutter/impeller/toolkit/interop/image_filter.cc + ../../../flutter/LICENSE
5176751769
ORIGIN: ../../../flutter/impeller/toolkit/interop/image_filter.h + ../../../flutter/LICENSE
5176851770
ORIGIN: ../../../flutter/impeller/toolkit/interop/impeller.cc + ../../../flutter/LICENSE
5176951771
ORIGIN: ../../../flutter/impeller/toolkit/interop/impeller.h + ../../../flutter/LICENSE
5177051772
ORIGIN: ../../../flutter/impeller/toolkit/interop/impeller.hpp + ../../../flutter/LICENSE
5177151773
ORIGIN: ../../../flutter/impeller/toolkit/interop/impeller_c.c + ../../../flutter/LICENSE
5177251774
ORIGIN: ../../../flutter/impeller/toolkit/interop/impeller_cc.cc + ../../../flutter/LICENSE
51775+
ORIGIN: ../../../flutter/impeller/toolkit/interop/line_metrics.cc + ../../../flutter/LICENSE
51776+
ORIGIN: ../../../flutter/impeller/toolkit/interop/line_metrics.h + ../../../flutter/LICENSE
5177351777
ORIGIN: ../../../flutter/impeller/toolkit/interop/mask_filter.cc + ../../../flutter/LICENSE
5177451778
ORIGIN: ../../../flutter/impeller/toolkit/interop/mask_filter.h + ../../../flutter/LICENSE
5177551779
ORIGIN: ../../../flutter/impeller/toolkit/interop/object.cc + ../../../flutter/LICENSE
@@ -54745,13 +54749,17 @@ FILE: ../../../flutter/impeller/toolkit/interop/example_mtl.m
5474554749
FILE: ../../../flutter/impeller/toolkit/interop/example_vk.c
5474654750
FILE: ../../../flutter/impeller/toolkit/interop/formats.cc
5474754751
FILE: ../../../flutter/impeller/toolkit/interop/formats.h
54752+
FILE: ../../../flutter/impeller/toolkit/interop/glyph_info.cc
54753+
FILE: ../../../flutter/impeller/toolkit/interop/glyph_info.h
5474854754
FILE: ../../../flutter/impeller/toolkit/interop/image_filter.cc
5474954755
FILE: ../../../flutter/impeller/toolkit/interop/image_filter.h
5475054756
FILE: ../../../flutter/impeller/toolkit/interop/impeller.cc
5475154757
FILE: ../../../flutter/impeller/toolkit/interop/impeller.h
5475254758
FILE: ../../../flutter/impeller/toolkit/interop/impeller.hpp
5475354759
FILE: ../../../flutter/impeller/toolkit/interop/impeller_c.c
5475454760
FILE: ../../../flutter/impeller/toolkit/interop/impeller_cc.cc
54761+
FILE: ../../../flutter/impeller/toolkit/interop/line_metrics.cc
54762+
FILE: ../../../flutter/impeller/toolkit/interop/line_metrics.h
5475554763
FILE: ../../../flutter/impeller/toolkit/interop/mask_filter.cc
5475654764
FILE: ../../../flutter/impeller/toolkit/interop/mask_filter.h
5475754765
FILE: ../../../flutter/impeller/toolkit/interop/object.cc

engine/src/flutter/impeller/toolkit/interop/BUILD.gn

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,12 @@ impeller_component("interop_base") {
4141
"dl_builder.h",
4242
"formats.cc",
4343
"formats.h",
44+
"glyph_info.cc",
45+
"glyph_info.h",
4446
"image_filter.cc",
4547
"image_filter.h",
48+
"line_metrics.cc",
49+
"line_metrics.h",
4650
"mask_filter.cc",
4751
"mask_filter.h",
4852
"object.cc",
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/toolkit/interop/glyph_info.h"
6+
7+
namespace impeller::interop {
8+
9+
GlyphInfo::~GlyphInfo() = default;
10+
11+
size_t GlyphInfo::GetGraphemeClusterCodeUnitRangeBegin() const {
12+
return info_.fGraphemeClusterTextRange.start;
13+
}
14+
15+
size_t GlyphInfo::GetGraphemeClusterCodeUnitRangeEnd() const {
16+
return info_.fGraphemeClusterTextRange.end;
17+
}
18+
19+
ImpellerRect GlyphInfo::GetGraphemeClusterBounds() const {
20+
return ImpellerRect{
21+
info_.fGraphemeLayoutBounds.y(),
22+
info_.fGraphemeLayoutBounds.x(),
23+
info_.fGraphemeLayoutBounds.width(),
24+
info_.fGraphemeLayoutBounds.height(),
25+
};
26+
}
27+
28+
bool GlyphInfo::IsEllipsis() const {
29+
return info_.fIsEllipsis;
30+
}
31+
32+
ImpellerTextDirection GlyphInfo::GetTextDirection() const {
33+
switch (info_.fDirection) {
34+
case skia::textlayout::TextDirection::kRtl:
35+
return kImpellerTextDirectionRTL;
36+
case skia::textlayout::TextDirection::kLtr:
37+
return kImpellerTextDirectionLTR;
38+
}
39+
return kImpellerTextDirectionLTR;
40+
}
41+
42+
} // namespace impeller::interop
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_IMPELLER_TOOLKIT_INTEROP_GLYPH_INFO_H_
6+
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_GLYPH_INFO_H_
7+
8+
#include "flutter/third_party/skia/modules/skparagraph/include/Paragraph.h"
9+
#include "impeller/toolkit/interop/impeller.h"
10+
#include "impeller/toolkit/interop/object.h"
11+
12+
namespace impeller::interop {
13+
14+
//------------------------------------------------------------------------------
15+
/// @brief Internal C++ peer of ImpellerGlyphInfo. For detailed
16+
/// documentation, refer to the headerdocs in the public API in
17+
/// impeller.h.
18+
///
19+
class GlyphInfo final
20+
: public Object<GlyphInfo,
21+
IMPELLER_INTERNAL_HANDLE_NAME(ImpellerGlyphInfo)> {
22+
public:
23+
explicit GlyphInfo(skia::textlayout::Paragraph::GlyphInfo info)
24+
: info_(info) {}
25+
26+
~GlyphInfo();
27+
28+
GlyphInfo(const GlyphInfo&) = delete;
29+
30+
GlyphInfo& operator=(const GlyphInfo&) = delete;
31+
32+
//----------------------------------------------------------------------------
33+
/// @see ImpellerGlyphInfoGetGraphemeClusterCodeUnitRangeBegin.
34+
///
35+
size_t GetGraphemeClusterCodeUnitRangeBegin() const;
36+
37+
//----------------------------------------------------------------------------
38+
/// @see ImpellerGlyphInfoGetGraphemeClusterCodeUnitRangeEnd.
39+
///
40+
size_t GetGraphemeClusterCodeUnitRangeEnd() const;
41+
42+
//----------------------------------------------------------------------------
43+
/// @see ImpellerGlyphInfoGetGraphemeClusterBounds.
44+
///
45+
ImpellerRect GetGraphemeClusterBounds() const;
46+
47+
//----------------------------------------------------------------------------
48+
/// @see ImpellerGlyphInfoIsEllipsis.
49+
///
50+
bool IsEllipsis() const;
51+
52+
//----------------------------------------------------------------------------
53+
/// @see ImpellerGlyphInfoGetTextDirection.
54+
///
55+
ImpellerTextDirection GetTextDirection() const;
56+
57+
private:
58+
const skia::textlayout::Paragraph::GlyphInfo info_;
59+
};
60+
61+
} // namespace impeller::interop
62+
63+
#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_GLYPH_INFO_H_

engine/src/flutter/impeller/toolkit/interop/impeller.cc

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
#include "impeller/toolkit/interop/context.h"
1818
#include "impeller/toolkit/interop/dl_builder.h"
1919
#include "impeller/toolkit/interop/formats.h"
20+
#include "impeller/toolkit/interop/glyph_info.h"
2021
#include "impeller/toolkit/interop/image_filter.h"
22+
#include "impeller/toolkit/interop/line_metrics.h"
2123
#include "impeller/toolkit/interop/mask_filter.h"
2224
#include "impeller/toolkit/interop/object.h"
2325
#include "impeller/toolkit/interop/paint.h"
@@ -58,7 +60,9 @@ DEFINE_PEER_GETTER(ColorSource, ImpellerColorSource);
5860
DEFINE_PEER_GETTER(Context, ImpellerContext);
5961
DEFINE_PEER_GETTER(DisplayList, ImpellerDisplayList);
6062
DEFINE_PEER_GETTER(DisplayListBuilder, ImpellerDisplayListBuilder);
63+
DEFINE_PEER_GETTER(GlyphInfo, ImpellerGlyphInfo);
6164
DEFINE_PEER_GETTER(ImageFilter, ImpellerImageFilter);
65+
DEFINE_PEER_GETTER(LineMetrics, ImpellerLineMetrics);
6266
DEFINE_PEER_GETTER(MaskFilter, ImpellerMaskFilter);
6367
DEFINE_PEER_GETTER(Paint, ImpellerPaint);
6468
DEFINE_PEER_GETTER(Paragraph, ImpellerParagraph);
@@ -1272,6 +1276,12 @@ uint32_t ImpellerParagraphGetLineCount(ImpellerParagraph paragraph) {
12721276
return GetPeer(paragraph)->GetLineCount();
12731277
}
12741278

1279+
IMPELLER_EXTERN_C
1280+
ImpellerRange ImpellerParagraphGetWordBoundary(ImpellerParagraph paragraph,
1281+
size_t code_unit_index) {
1282+
return GetPeer(paragraph)->GetWordBoundary(code_unit_index);
1283+
}
1284+
12751285
IMPELLER_EXTERN_C
12761286
ImpellerTypographyContext ImpellerTypographyContextNew() {
12771287
auto context = Create<TypographyContext>();
@@ -1309,4 +1319,154 @@ bool ImpellerTypographyContextRegisterFont(ImpellerTypographyContext context,
13091319
family_name_alias);
13101320
}
13111321

1322+
IMPELLER_EXTERN_C
1323+
ImpellerLineMetrics ImpellerParagraphGetLineMetrics(
1324+
ImpellerParagraph paragraph) {
1325+
return GetPeer(paragraph)->GetLineMetrics().GetC();
1326+
}
1327+
1328+
IMPELLER_EXTERN_C
1329+
ImpellerGlyphInfo ImpellerParagraphCreateGlyphInfoAtCodeUnitIndexNew(
1330+
ImpellerParagraph paragraph,
1331+
size_t code_unit_index) {
1332+
return GetPeer(paragraph)
1333+
->GetGlyphInfoAtCodeUnitIndex(code_unit_index)
1334+
.Leak();
1335+
}
1336+
1337+
IMPELLER_EXTERN_C
1338+
ImpellerGlyphInfo ImpellerParagraphCreateGlyphInfoAtParagraphCoordinatesNew(
1339+
ImpellerParagraph paragraph,
1340+
double x,
1341+
double y) {
1342+
return GetPeer(paragraph)
1343+
->GetClosestGlyphInfoAtParagraphCoordinates(x, y)
1344+
.Leak();
1345+
}
1346+
1347+
//------------------------------------------------------------------------------
1348+
// Line Metrics
1349+
//------------------------------------------------------------------------------
1350+
1351+
IMPELLER_EXTERN_C
1352+
void ImpellerLineMetricsRetain(ImpellerLineMetrics line_metrics) {
1353+
ObjectBase::SafeRetain(line_metrics);
1354+
}
1355+
1356+
IMPELLER_EXTERN_C
1357+
void ImpellerLineMetricsRelease(ImpellerLineMetrics line_metrics) {
1358+
ObjectBase::SafeRelease(line_metrics);
1359+
}
1360+
1361+
IMPELLER_EXTERN_C
1362+
double ImpellerLineMetricsGetUnscaledAscent(ImpellerLineMetrics metrics,
1363+
size_t line) {
1364+
return GetPeer(metrics)->GetUnscaledAscent(line);
1365+
}
1366+
1367+
IMPELLER_EXTERN_C
1368+
double ImpellerLineMetricsGetAscent(ImpellerLineMetrics metrics, size_t line) {
1369+
return GetPeer(metrics)->GetAscent(line);
1370+
}
1371+
1372+
IMPELLER_EXTERN_C
1373+
double ImpellerLineMetricsGetDescent(ImpellerLineMetrics metrics, size_t line) {
1374+
return GetPeer(metrics)->GetDescent(line);
1375+
}
1376+
1377+
IMPELLER_EXTERN_C
1378+
double ImpellerLineMetricsGetBaseline(ImpellerLineMetrics metrics,
1379+
size_t line) {
1380+
return GetPeer(metrics)->GetBaseline(line);
1381+
}
1382+
1383+
IMPELLER_EXTERN_C
1384+
bool ImpellerLineMetricsIsHardbreak(ImpellerLineMetrics metrics, size_t line) {
1385+
return GetPeer(metrics)->IsHardbreak(line);
1386+
}
1387+
1388+
IMPELLER_EXTERN_C
1389+
double ImpellerLineMetricsGetWidth(ImpellerLineMetrics metrics, size_t line) {
1390+
return GetPeer(metrics)->GetWidth(line);
1391+
}
1392+
1393+
IMPELLER_EXTERN_C
1394+
double ImpellerLineMetricsGetHeight(ImpellerLineMetrics metrics, size_t line) {
1395+
return GetPeer(metrics)->GetHeight(line);
1396+
}
1397+
1398+
IMPELLER_EXTERN_C
1399+
double ImpellerLineMetricsGetLeft(ImpellerLineMetrics metrics, size_t line) {
1400+
return GetPeer(metrics)->GetLeft(line);
1401+
}
1402+
1403+
IMPELLER_EXTERN_C
1404+
size_t ImpellerLineMetricsGetCodeUnitStartIndex(ImpellerLineMetrics metrics,
1405+
size_t line) {
1406+
return GetPeer(metrics)->GetCodeUnitStartIndex(line);
1407+
}
1408+
1409+
IMPELLER_EXTERN_C
1410+
size_t ImpellerLineMetricsGetCodeUnitEndIndex(ImpellerLineMetrics metrics,
1411+
size_t line) {
1412+
return GetPeer(metrics)->GetCodeUnitEndIndex(line);
1413+
}
1414+
1415+
IMPELLER_EXTERN_C
1416+
size_t ImpellerLineMetricsGetCodeUnitEndIndexExcludingWhitespace(
1417+
ImpellerLineMetrics metrics,
1418+
size_t line) {
1419+
return GetPeer(metrics)->GetCodeUnitEndIndexExcludingWhitespace(line);
1420+
}
1421+
1422+
IMPELLER_EXTERN_C
1423+
size_t ImpellerLineMetricsGetCodeUnitEndIndexIncludingNewline(
1424+
ImpellerLineMetrics metrics,
1425+
size_t line) {
1426+
return GetPeer(metrics)->GetCodeUnitEndIndexIncludingNewline(line);
1427+
}
1428+
1429+
//------------------------------------------------------------------------------
1430+
// Glyph Info
1431+
//------------------------------------------------------------------------------
1432+
1433+
IMPELLER_EXTERN_C
1434+
void ImpellerGlyphInfoRetain(ImpellerGlyphInfo glyph_info) {
1435+
ObjectBase::SafeRetain(glyph_info);
1436+
}
1437+
1438+
IMPELLER_EXTERN_C
1439+
void ImpellerGlyphInfoRelease(ImpellerGlyphInfo glyph_info) {
1440+
ObjectBase::SafeRelease(glyph_info);
1441+
}
1442+
1443+
IMPELLER_EXTERN_C
1444+
size_t ImpellerGlyphInfoGetGraphemeClusterCodeUnitRangeBegin(
1445+
ImpellerGlyphInfo glyph_info) {
1446+
return GetPeer(glyph_info)->GetGraphemeClusterCodeUnitRangeBegin();
1447+
}
1448+
1449+
IMPELLER_EXTERN_C
1450+
size_t ImpellerGlyphInfoGetGraphemeClusterCodeUnitRangeEnd(
1451+
ImpellerGlyphInfo glyph_info) {
1452+
return GetPeer(glyph_info)->GetGraphemeClusterCodeUnitRangeEnd();
1453+
}
1454+
1455+
IMPELLER_EXTERN_C
1456+
ImpellerRect ImpellerGlyphInfoGetGraphemeClusterBounds(
1457+
ImpellerGlyphInfo glyph_info) {
1458+
return GetPeer(glyph_info)->GetGraphemeClusterBounds();
1459+
}
1460+
1461+
IMPELLER_EXTERN_C
1462+
bool ImpellerGlyphInfoIsEllipsis(ImpellerGlyphInfo glyph_info) {
1463+
return GetPeer(glyph_info)->IsEllipsis();
1464+
}
1465+
1466+
IMPELLER_EXTERN_C
1467+
ImpellerTextDirection ImpellerGlyphInfoGetTextDirection(
1468+
ImpellerGlyphInfo glyph_info) {
1469+
return GetPeer(glyph_info)->GetTextDirection();
1470+
}
1471+
13121472
} // namespace impeller::interop

0 commit comments

Comments
 (0)