Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
[Impeller] libImpeller: Implement APIs for fetching glyph and line me…
…trics.

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
  • Loading branch information
chinmaygarde committed Mar 21, 2025
commit 5b17a0c964ac459c30228ea903fb28a5b3f224ff
4 changes: 4 additions & 0 deletions engine/src/flutter/impeller/toolkit/interop/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ impeller_component("interop_base") {
"dl_builder.h",
"formats.cc",
"formats.h",
"glyph_info.cc",
"glyph_info.h",
"image_filter.cc",
"image_filter.h",
"line_metrics.cc",
"line_metrics.h",
"mask_filter.cc",
"mask_filter.h",
"object.cc",
Expand Down
42 changes: 42 additions & 0 deletions engine/src/flutter/impeller/toolkit/interop/glyph_info.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "impeller/toolkit/interop/glyph_info.h"

namespace impeller::interop {

GlyphInfo::~GlyphInfo() = default;

size_t GlyphInfo::GetGraphemeClusterCodeUnitRangeBegin() const {
return info_.fGraphemeClusterTextRange.start;
}

size_t GlyphInfo::GetGraphemeClusterCodeUnitRangeEnd() const {
return info_.fGraphemeClusterTextRange.end;
}

ImpellerRect GlyphInfo::GetGraphemeClusterBounds() const {
return ImpellerRect{
info_.fGraphemeLayoutBounds.y(),
info_.fGraphemeLayoutBounds.x(),
info_.fGraphemeLayoutBounds.width(),
info_.fGraphemeLayoutBounds.height(),
};
}

bool GlyphInfo::IsEllipsis() const {
return info_.fIsEllipsis;
}

ImpellerTextDirection GlyphInfo::GetTextDirection() const {
switch (info_.fDirection) {
case skia::textlayout::TextDirection::kRtl:
return kImpellerTextDirectionRTL;
case skia::textlayout::TextDirection::kLtr:
return kImpellerTextDirectionLTR;
}
return kImpellerTextDirectionLTR;
}

} // namespace impeller::interop
63 changes: 63 additions & 0 deletions engine/src/flutter/impeller/toolkit/interop/glyph_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_IMPELLER_TOOLKIT_INTEROP_GLYPH_INFO_H_
#define FLUTTER_IMPELLER_TOOLKIT_INTEROP_GLYPH_INFO_H_

#include "flutter/third_party/skia/modules/skparagraph/include/Paragraph.h"
#include "impeller/toolkit/interop/impeller.h"
#include "impeller/toolkit/interop/object.h"

namespace impeller::interop {

//------------------------------------------------------------------------------
/// @brief Internal C++ peer of ImpellerGlyphInfo. For detailed
/// documentation, refer to the headerdocs in the public API in
/// impeller.h.
///
class GlyphInfo final
: public Object<GlyphInfo,
IMPELLER_INTERNAL_HANDLE_NAME(ImpellerGlyphInfo)> {
public:
GlyphInfo(skia::textlayout::Paragraph::GlyphInfo info)
: info_(std::move(info)) {}

~GlyphInfo();

GlyphInfo(const GlyphInfo&) = delete;

GlyphInfo& operator=(const GlyphInfo&) = delete;

//----------------------------------------------------------------------------
/// @see ImpellerGlpyhInfoGetGraphemeClusterCodeUnitRangeBegin.
///
size_t GetGraphemeClusterCodeUnitRangeBegin() const;

//----------------------------------------------------------------------------
/// @see ImpellerGlpyhInfoGetGraphemeClusterCodeUnitRangeEnd.
///
size_t GetGraphemeClusterCodeUnitRangeEnd() const;

//----------------------------------------------------------------------------
/// @see ImpellerGlpyhInfoGetGraphemeClusterBounds.
///
ImpellerRect GetGraphemeClusterBounds() const;

//----------------------------------------------------------------------------
/// @see ImpellerGlyphInfoIsEllipsis.
///
bool IsEllipsis() const;

//----------------------------------------------------------------------------
/// @see ImpellerGlyphInfoGetTextDirection.
///
ImpellerTextDirection GetTextDirection() const;

private:
const skia::textlayout::Paragraph::GlyphInfo info_;
};

} // namespace impeller::interop

#endif // FLUTTER_IMPELLER_TOOLKIT_INTEROP_GLYPH_INFO_H_
161 changes: 161 additions & 0 deletions engine/src/flutter/impeller/toolkit/interop/impeller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
#include "impeller/toolkit/interop/context.h"
#include "impeller/toolkit/interop/dl_builder.h"
#include "impeller/toolkit/interop/formats.h"
#include "impeller/toolkit/interop/glyph_info.h"
#include "impeller/toolkit/interop/image_filter.h"
#include "impeller/toolkit/interop/line_metrics.h"
#include "impeller/toolkit/interop/mask_filter.h"
#include "impeller/toolkit/interop/object.h"
#include "impeller/toolkit/interop/paint.h"
Expand Down Expand Up @@ -58,7 +60,9 @@ DEFINE_PEER_GETTER(ColorSource, ImpellerColorSource);
DEFINE_PEER_GETTER(Context, ImpellerContext);
DEFINE_PEER_GETTER(DisplayList, ImpellerDisplayList);
DEFINE_PEER_GETTER(DisplayListBuilder, ImpellerDisplayListBuilder);
DEFINE_PEER_GETTER(GlyphInfo, ImpellerGlyphInfo);
DEFINE_PEER_GETTER(ImageFilter, ImpellerImageFilter);
DEFINE_PEER_GETTER(LineMetrics, ImpellerLineMetrics);
DEFINE_PEER_GETTER(MaskFilter, ImpellerMaskFilter);
DEFINE_PEER_GETTER(Paint, ImpellerPaint);
DEFINE_PEER_GETTER(Paragraph, ImpellerParagraph);
Expand Down Expand Up @@ -1272,6 +1276,12 @@ uint32_t ImpellerParagraphGetLineCount(ImpellerParagraph paragraph) {
return GetPeer(paragraph)->GetLineCount();
}

IMPELLER_EXTERN_C
ImpellerRange ImpellerParagraphGetWordBoundary(ImpellerParagraph paragraph,
size_t code_unit_index) {
return GetPeer(paragraph)->GetWordBoundary(code_unit_index);
}

IMPELLER_EXTERN_C
ImpellerTypographyContext ImpellerTypographyContextNew() {
auto context = Create<TypographyContext>();
Expand Down Expand Up @@ -1309,4 +1319,155 @@ bool ImpellerTypographyContextRegisterFont(ImpellerTypographyContext context,
family_name_alias);
}

IMPELLER_EXTERN_C
ImpellerLineMetrics ImpellerParagraphGetLineMetrics(
ImpellerParagraph paragraph) {
return GetPeer(paragraph)->GetLineMetrics().GetC();
}

IMPELLER_EXTERN_C
ImpellerGlyphInfo ImpellerParagraphCreateGlyphInfoAtCodeUnitIndexNew(
ImpellerParagraph paragraph,
size_t code_unit_index) {
return GetPeer(paragraph)
->GetGlyphInfoAtCodeUnitIndex(code_unit_index)
.Leak();
}

IMPELLER_EXTERN_C
ImpellerGlyphInfo ImpellerParagraphCreateGlyphInfoAtParagraphCoordinatesNew(
ImpellerParagraph paragraph,
double x,
double y) {
return GetPeer(paragraph)
->GetClosestGlyphInfoAtParagraphCoordinates(x, y)
.Leak();
}

//------------------------------------------------------------------------------
// Line Metrics
//------------------------------------------------------------------------------

IMPELLER_EXTERN_C
void ImpellerLineMetricsRetain(ImpellerLineMetrics line_metrics) {
ObjectBase::SafeRetain(line_metrics);
}

IMPELLER_EXTERN_C
void ImpellerLineMetricsRelease(ImpellerLineMetrics line_metrics) {
ObjectBase::SafeRelease(line_metrics);
}

IMPELLER_EXTERN_C
double ImpellerLineMetricsGetUnscaledAscent(ImpellerLineMetrics metrics,
size_t line) {
return GetPeer(metrics)->GetUnscaledAscent(line);
}

IMPELLER_EXTERN_C
double ImpellerLineMetricsGetAscent(ImpellerLineMetrics metrics, size_t line) {
return GetPeer(metrics)->GetAscent(line);
}

IMPELLER_EXTERN_C
double ImpellerLineMetricsGetDescent(ImpellerLineMetrics metrics, size_t line) {
return GetPeer(metrics)->GetDescent(line);
}

IMPELLER_EXTERN_C
double ImpellerLineMetricsGetBaseline(ImpellerLineMetrics metrics,
size_t line) {
return GetPeer(metrics)->GetBaseline(line);
}

IMPELLER_EXTERN_C
bool ImpellerLineMetricsGetIsHardbreak(ImpellerLineMetrics metrics,
size_t line) {
return GetPeer(metrics)->GetIsHardbreak(line);
}

IMPELLER_EXTERN_C
double ImpellerLineMetricsGetWidth(ImpellerLineMetrics metrics, size_t line) {
return GetPeer(metrics)->GetWidth(line);
}

IMPELLER_EXTERN_C
double ImpellerLineMetricsGetHeight(ImpellerLineMetrics metrics, size_t line) {
return GetPeer(metrics)->GetHeight(line);
}

IMPELLER_EXTERN_C
double ImpellerLineMetricsGetLeft(ImpellerLineMetrics metrics, size_t line) {
return GetPeer(metrics)->GetLeft(line);
}

IMPELLER_EXTERN_C
size_t ImpellerLineMetricsGetCodeUnitStartIndex(ImpellerLineMetrics metrics,
size_t line) {
return GetPeer(metrics)->GetCodeUnitStartIndex(line);
}

IMPELLER_EXTERN_C
size_t ImpellerLineMetricsGetCodeUnitEndIndex(ImpellerLineMetrics metrics,
size_t line) {
return GetPeer(metrics)->GetCodeUnitEndIndex(line);
}

IMPELLER_EXTERN_C
size_t ImpellerLineMetricsGetCodeUnitEndIndexExcludingWhitespace(
ImpellerLineMetrics metrics,
size_t line) {
return GetPeer(metrics)->GetCodeUnitEndIndexExcludingWhitespace(line);
}

IMPELLER_EXTERN_C
size_t ImpellerLineMetricsGetCodeUnitEndIndexIncludingNewline(
ImpellerLineMetrics metrics,
size_t line) {
return GetPeer(metrics)->GetCodeUnitEndIndexIncludingNewline(line);
}

//------------------------------------------------------------------------------
// Glyph Info
//------------------------------------------------------------------------------

IMPELLER_EXTERN_C
void ImpellerGlyphInfoRetain(ImpellerGlyphInfo glyph_info) {
ObjectBase::SafeRetain(glyph_info);
}

IMPELLER_EXTERN_C
void ImpellerGlyphInfoRelease(ImpellerGlyphInfo glyph_info) {
ObjectBase::SafeRelease(glyph_info);
}

IMPELLER_EXTERN_C
size_t ImpellerGlyphInfoGetGraphemeClusterCodeUnitRangeBegin(
ImpellerGlyphInfo glyph_info) {
return GetPeer(glyph_info)->GetGraphemeClusterCodeUnitRangeBegin();
}

IMPELLER_EXTERN_C
size_t ImpellerGlyphInfoGetGraphemeClusterCodeUnitRangeEnd(
ImpellerGlyphInfo glyph_info) {
return GetPeer(glyph_info)->GetGraphemeClusterCodeUnitRangeEnd();
}

IMPELLER_EXTERN_C
ImpellerRect ImpellerGlyphInfoGetGraphemeClusterBounds(
ImpellerGlyphInfo glyph_info) {
return GetPeer(glyph_info)->GetGraphemeClusterBounds();
}

IMPELLER_EXTERN_C
bool ImpellerGlyphInfoIsEllipsis(ImpellerGlyphInfo glyph_info) {
return GetPeer(glyph_info)->IsEllipsis();
}

IMPELLER_EXTERN_C
ImpellerTextDirection ImpellerGlyphInfoGetTextDirection(
ImpellerGlyphInfo glyph_info) {
return GetPeer(glyph_info)->GetTextDirection();
}

} // namespace impeller::interop
Loading