Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 24ee4e0

Browse files
Ben WagnerSkia Commit-Bot
authored andcommitted
Add feature support to shaper.
Change-Id: Iab8bf9ab144d1661c0653b617ca394c3663ab35c Reviewed-on: https://skia-review.googlesource.com/c/skia/+/248861 Commit-Queue: Ben Wagner <bungeman@google.com> Reviewed-by: Julia Lavrova <jlavrova@google.com>
1 parent 027f89b commit 24ee4e0

File tree

3 files changed

+120
-19
lines changed

3 files changed

+120
-19
lines changed

modules/skshaper/include/SkShaper.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ class SkShaper {
6969
/** Should be BCP-47, c locale names may also work. */
7070
virtual const char* currentLanguage() const = 0;
7171
};
72+
struct Feature {
73+
SkFourByteTag tag;
74+
uint32_t value;
75+
size_t start; // Offset to the start (utf8) element of the run.
76+
size_t end; // Offset to one past the last (utf8) element of the run.
77+
};
7278

7379
private:
7480
template <typename RunIteratorSubclass>
@@ -207,6 +213,15 @@ class SkShaper {
207213
SkScalar width,
208214
RunHandler*) const = 0;
209215

216+
virtual void shape(const char* utf8, size_t utf8Bytes,
217+
FontRunIterator&,
218+
BiDiRunIterator&,
219+
ScriptRunIterator&,
220+
LanguageRunIterator&,
221+
const Feature* features, size_t featuresSize,
222+
SkScalar width,
223+
RunHandler*) const = 0;
224+
210225
private:
211226
SkShaper(const SkShaper&) = delete;
212227
SkShaper& operator=(const SkShaper&) = delete;

modules/skshaper/src/SkShaper_harfbuzz.cpp

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "include/private/SkTo.h"
2727
#include "modules/skshaper/include/SkShaper.h"
2828
#include "src/core/SkMakeUnique.h"
29+
#include "src/core/SkSpan.h"
2930
#include "src/core/SkTDPQueue.h"
3031
#include "src/utils/SkUTF.h"
3132

@@ -50,20 +51,28 @@
5051
#include "SkLoadICU.h"
5152
#endif
5253

54+
// HB_FEATURE_GLOBAL_START and HB_FEATURE_GLOBAL_END were not added until HarfBuzz 2.0
55+
// They would have always worked, they just hadn't been named yet.
56+
#if !defined(HB_FEATURE_GLOBAL_START)
57+
# define HB_FEATURE_GLOBAL_START 0
58+
#endif
59+
#if !defined(HB_FEATURE_GLOBAL_END)
60+
# define HB_FEATURE_GLOBAL_END ((unsigned int) -1)
61+
#endif
62+
5363
namespace skstd {
5464
template <> struct is_bitmask_enum<hb_buffer_flags_t> : std::true_type {};
5565
}
5666

5767
namespace {
58-
template <typename T, void(*P)(T*)> using resource =
59-
std::unique_ptr<T, SkFunctionWrapper<skstd::remove_pointer_t<decltype(P)>, P>>;
60-
using HBBlob = resource<hb_blob_t , &hb_blob_destroy >;
61-
using HBFace = resource<hb_face_t , &hb_face_destroy >;
62-
using HBFont = resource<hb_font_t , &hb_font_destroy >;
63-
using HBBuffer = resource<hb_buffer_t , &hb_buffer_destroy>;
64-
using ICUBiDi = resource<UBiDi , &ubidi_close >;
65-
using ICUBrk = resource<UBreakIterator, &ubrk_close >;
66-
using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>>;
68+
template <typename T,typename P,P* p> using resource = std::unique_ptr<T, SkFunctionWrapper<P, p>>;
69+
using HBBlob = resource<hb_blob_t , decltype(hb_blob_destroy) , hb_blob_destroy >;
70+
using HBFace = resource<hb_face_t , decltype(hb_face_destroy) , hb_face_destroy >;
71+
using HBFont = resource<hb_font_t , decltype(hb_font_destroy) , hb_font_destroy >;
72+
using HBBuffer = resource<hb_buffer_t , decltype(hb_buffer_destroy), hb_buffer_destroy>;
73+
using ICUBiDi = resource<UBiDi , decltype(ubidi_close) , ubidi_close >;
74+
using ICUBrk = resource<UBreakIterator, decltype(ubrk_close) , ubrk_close >;
75+
using ICUUText = resource<UText , decltype(utext_close) , utext_close >;
6776

6877
HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
6978
size_t size = asset->getLength();
@@ -641,7 +650,8 @@ class ShaperHarfBuzz : public SkShaper {
641650
const BiDiRunIterator&,
642651
const LanguageRunIterator&,
643652
const ScriptRunIterator&,
644-
const FontRunIterator&) const;
653+
const FontRunIterator&,
654+
const Feature*, size_t featuresSize) const;
645655
private:
646656
const sk_sp<SkFontMgr> fFontMgr;
647657
HBBuffer fBuffer;
@@ -660,12 +670,22 @@ class ShaperHarfBuzz : public SkShaper {
660670
SkScalar width,
661671
RunHandler*) const override;
662672

673+
void shape(const char* utf8Text, size_t textBytes,
674+
FontRunIterator&,
675+
BiDiRunIterator&,
676+
ScriptRunIterator&,
677+
LanguageRunIterator&,
678+
const Feature*, size_t featuresSize,
679+
SkScalar width,
680+
RunHandler*) const override;
681+
663682
virtual void wrap(char const * const utf8, size_t utf8Bytes,
664683
const BiDiRunIterator&,
665684
const LanguageRunIterator&,
666685
const ScriptRunIterator&,
667686
const FontRunIterator&,
668687
RunIteratorQueue& runSegmenter,
688+
const Feature*, size_t featuresSize,
669689
SkScalar width,
670690
RunHandler*) const = 0;
671691
};
@@ -680,6 +700,7 @@ class ShaperDrivenWrapper : public ShaperHarfBuzz {
680700
const ScriptRunIterator&,
681701
const FontRunIterator&,
682702
RunIteratorQueue& runSegmenter,
703+
const Feature*, size_t featuresSize,
683704
SkScalar width,
684705
RunHandler*) const override;
685706
};
@@ -694,6 +715,7 @@ class ShapeThenWrap : public ShaperHarfBuzz {
694715
const ScriptRunIterator&,
695716
const FontRunIterator&,
696717
RunIteratorQueue& runSegmenter,
718+
const Feature*, size_t featuresSize,
697719
SkScalar width,
698720
RunHandler*) const override;
699721
};
@@ -708,6 +730,7 @@ class ShapeDontWrapOrReorder : public ShaperHarfBuzz {
708730
const ScriptRunIterator&,
709731
const FontRunIterator&,
710732
RunIteratorQueue& runSegmenter,
733+
const Feature*, size_t featuresSize,
711734
SkScalar width,
712735
RunHandler*) const override;
713736
};
@@ -799,6 +822,18 @@ void ShaperHarfBuzz::shape(const char* utf8, size_t utf8Bytes,
799822
LanguageRunIterator& language,
800823
SkScalar width,
801824
RunHandler* handler) const
825+
{
826+
this->shape(utf8, utf8Bytes, font, bidi, script, language, nullptr, 0, width, handler);
827+
}
828+
829+
void ShaperHarfBuzz::shape(const char* utf8, size_t utf8Bytes,
830+
FontRunIterator& font,
831+
BiDiRunIterator& bidi,
832+
ScriptRunIterator& script,
833+
LanguageRunIterator& language,
834+
const Feature* features, size_t featuresSize,
835+
SkScalar width,
836+
RunHandler* handler) const
802837
{
803838
SkASSERT(handler);
804839
RunIteratorQueue runSegmenter;
@@ -807,7 +842,8 @@ void ShaperHarfBuzz::shape(const char* utf8, size_t utf8Bytes,
807842
runSegmenter.insert(&script, 1);
808843
runSegmenter.insert(&language, 0);
809844

810-
this->wrap(utf8, utf8Bytes, bidi, language, script, font, runSegmenter, width, handler);
845+
this->wrap(utf8, utf8Bytes, bidi, language, script, font, runSegmenter,
846+
features, featuresSize, width, handler);
811847
}
812848

813849
void ShaperDrivenWrapper::wrap(char const * const utf8, size_t utf8Bytes,
@@ -816,6 +852,7 @@ void ShaperDrivenWrapper::wrap(char const * const utf8, size_t utf8Bytes,
816852
const ScriptRunIterator& script,
817853
const FontRunIterator& font,
818854
RunIteratorQueue& runSegmenter,
855+
const Feature* features, size_t featuresSize,
819856
SkScalar width,
820857
RunHandler* handler) const
821858
{
@@ -845,7 +882,8 @@ void ShaperDrivenWrapper::wrap(char const * const utf8, size_t utf8Bytes,
845882
if (modelNeedsRegenerated) {
846883
model = shape(utf8, utf8Bytes,
847884
utf8Start, utf8End,
848-
bidi, language, script, font);
885+
bidi, language, script, font,
886+
features, featuresSize);
849887
modelGlyphOffset = 0;
850888

851889
SkVector advance = {0, 0};
@@ -911,7 +949,8 @@ void ShaperDrivenWrapper::wrap(char const * const utf8, size_t utf8Bytes,
911949
} else {
912950
return shape(utf8, utf8Bytes,
913951
utf8Start, utf8Start + breakIteratorCurrent,
914-
bidi, language, script, font);
952+
bidi, language, script, font,
953+
features, featuresSize);
915954
}
916955
}(modelText[breakIteratorCurrent + modelTextOffset]);
917956
auto score = [widthLeft](const ShapedRun& run) -> SkScalar {
@@ -966,6 +1005,7 @@ void ShapeThenWrap::wrap(char const * const utf8, size_t utf8Bytes,
9661005
const ScriptRunIterator& script,
9671006
const FontRunIterator& font,
9681007
RunIteratorQueue& runSegmenter,
1008+
const Feature* features, size_t featuresSize,
9691009
SkScalar width,
9701010
RunHandler* handler) const
9711011
{
@@ -1002,7 +1042,8 @@ void ShapeThenWrap::wrap(char const * const utf8, size_t utf8Bytes,
10021042

10031043
runs.emplace_back(shape(utf8, utf8Bytes,
10041044
utf8Start, utf8End,
1005-
bidi, language, script, font));
1045+
bidi, language, script, font,
1046+
features, featuresSize));
10061047
ShapedRun& run = runs.back();
10071048

10081049
uint32_t previousCluster = 0xFFFFFFFF;
@@ -1192,6 +1233,7 @@ void ShapeDontWrapOrReorder::wrap(char const * const utf8, size_t utf8Bytes,
11921233
const ScriptRunIterator& script,
11931234
const FontRunIterator& font,
11941235
RunIteratorQueue& runSegmenter,
1236+
const Feature* features, size_t featuresSize,
11951237
SkScalar width,
11961238
RunHandler* handler) const
11971239
{
@@ -1206,7 +1248,8 @@ void ShapeDontWrapOrReorder::wrap(char const * const utf8, size_t utf8Bytes,
12061248

12071249
runs.emplace_back(shape(utf8, utf8Bytes,
12081250
utf8Start, utf8End,
1209-
bidi, language, script, font));
1251+
bidi, language, script, font,
1252+
features, featuresSize));
12101253
}
12111254

12121255
handler->beginLine();
@@ -1237,11 +1280,12 @@ void ShapeDontWrapOrReorder::wrap(char const * const utf8, size_t utf8Bytes,
12371280
ShapedRun ShaperHarfBuzz::shape(char const * const utf8,
12381281
size_t const utf8Bytes,
12391282
char const * const utf8Start,
1240-
char const * const utf8End,
1283+
char const * const utf8End,
12411284
const BiDiRunIterator& bidi,
12421285
const LanguageRunIterator& language,
12431286
const ScriptRunIterator& script,
1244-
const FontRunIterator& font) const
1287+
const FontRunIterator& font,
1288+
Feature const * const features, size_t const featuresSize) const
12451289
{
12461290
size_t utf8runLength = utf8End - utf8Start;
12471291
ShapedRun run(RunHandler::Range(utf8Start - utf8, utf8runLength),
@@ -1274,14 +1318,32 @@ ShapedRun ShaperHarfBuzz::shape(char const * const utf8,
12741318
hb_buffer_set_script(buffer, hb_script_from_iso15924_tag((hb_tag_t)script.currentScript()));
12751319
hb_buffer_set_language(buffer, hb_language_from_string(language.currentLanguage(), -1));
12761320
hb_buffer_guess_segment_properties(buffer);
1277-
// TODO: features
12781321

12791322
// TODO: how to cache hbface (typeface) / hbfont (font)
12801323
HBFont hbFont(create_hb_font(font.currentFont()));
12811324
if (!hbFont) {
12821325
return run;
12831326
}
1284-
hb_shape(hbFont.get(), buffer, nullptr, 0);
1327+
1328+
SkSTArray<32, hb_feature_t> hbFeatures;
1329+
for (const auto& feature : SkMakeSpan(features, featuresSize)) {
1330+
if (feature.end < SkTo<size_t>(utf8Start - utf8) ||
1331+
SkTo<size_t>(utf8End - utf8) <= feature.start)
1332+
{
1333+
continue;
1334+
}
1335+
if (feature.start <= SkTo<size_t>(utf8Start - utf8) &&
1336+
SkTo<size_t>(utf8End - utf8) <= feature.end)
1337+
{
1338+
hbFeatures.push_back({ (hb_tag_t)feature.tag, feature.value,
1339+
HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END});
1340+
} else {
1341+
hbFeatures.push_back({ (hb_tag_t)feature.tag, feature.value,
1342+
SkTo<unsigned>(feature.start), SkTo<unsigned>(feature.end)});
1343+
}
1344+
}
1345+
1346+
hb_shape(hbFont.get(), buffer, hbFeatures.data(), hbFeatures.size());
12851347
unsigned len = hb_buffer_get_length(buffer);
12861348
if (len == 0) {
12871349
return run;

modules/skshaper/src/SkShaper_primitive.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ class SkShaperPrimitive : public SkShaper {
3030
LanguageRunIterator&,
3131
SkScalar width,
3232
RunHandler*) const override;
33+
34+
void shape(const char* utf8, size_t utf8Bytes,
35+
FontRunIterator&,
36+
BiDiRunIterator&,
37+
ScriptRunIterator&,
38+
LanguageRunIterator&,
39+
const Feature*, size_t featureSize,
40+
SkScalar width,
41+
RunHandler*) const override;
3342
};
3443

3544
std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
@@ -138,6 +147,21 @@ void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
138147
return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
139148
}
140149

150+
void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
151+
FontRunIterator& font,
152+
BiDiRunIterator& bidi,
153+
ScriptRunIterator&,
154+
LanguageRunIterator&,
155+
const Feature*, size_t,
156+
SkScalar width,
157+
RunHandler* handler) const {
158+
font.consume();
159+
SkASSERT(font.currentFont().getTypeface());
160+
bidi.consume();
161+
return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
162+
width, handler);
163+
}
164+
141165
void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
142166
const SkFont& font,
143167
bool leftToRight,

0 commit comments

Comments
 (0)