44
55#include < array>
66#include < cmath>
7+ #include < cstdlib>
78#include < iostream>
89#include < memory>
910#include < tuple>
1011#include < utility>
12+ #include < vector>
1113
1214#include " flutter/testing/testing.h"
1315#include " impeller/aiks/aiks_playground.h"
2931#include " impeller/scene/node.h"
3032#include " impeller/typographer/backends/skia/text_frame_skia.h"
3133#include " impeller/typographer/backends/skia/text_render_context_skia.h"
34+ #include " third_party/imgui/imgui.h"
3235#include " third_party/skia/include/core/SkData.h"
3336
3437namespace impeller {
@@ -1126,29 +1129,31 @@ static sk_sp<SkData> OpenFixtureAsSkData(const char* fixture_name) {
11261129 return data;
11271130}
11281131
1132+ struct TextRenderOptions {
1133+ Scalar font_size = 50 ;
1134+ Scalar alpha = 1 ;
1135+ Point position = Vector2(100 , 200 );
1136+ };
1137+
11291138bool RenderTextInCanvas (const std::shared_ptr<Context>& context,
11301139 Canvas& canvas,
11311140 const std::string& text,
11321141 const std::string& font_fixture,
1133- Scalar font_size = 50.0 ,
1134- Scalar alpha = 1.0 ) {
1135- Scalar baseline = 200.0 ;
1136- Point text_position = {100 , baseline};
1137-
1142+ TextRenderOptions options = {}) {
11381143 // Draw the baseline.
1139- canvas.DrawRect ({50 , baseline , 900 , 10 },
1144+ canvas.DrawRect ({options. position . x - 50 , options. position . y , 900 , 10 },
11401145 Paint{.color = Color::Aqua ().WithAlpha (0.25 )});
11411146
11421147 // Mark the point at which the text is drawn.
1143- canvas.DrawCircle (text_position , 5.0 ,
1148+ canvas.DrawCircle (options. position , 5.0 ,
11441149 Paint{.color = Color::Red ().WithAlpha (0.25 )});
11451150
11461151 // Construct the text blob.
11471152 auto mapping = OpenFixtureAsSkData (font_fixture.c_str ());
11481153 if (!mapping) {
11491154 return false ;
11501155 }
1151- SkFont sk_font (SkTypeface::MakeFromData (mapping), 50.0 );
1156+ SkFont sk_font (SkTypeface::MakeFromData (mapping), options. font_size );
11521157 auto blob = SkTextBlob::MakeFromString (text.c_str (), sk_font);
11531158 if (!blob) {
11541159 return false ;
@@ -1158,8 +1163,8 @@ bool RenderTextInCanvas(const std::shared_ptr<Context>& context,
11581163 auto frame = TextFrameFromTextBlob (blob);
11591164
11601165 Paint text_paint;
1161- text_paint.color = Color::Yellow ().WithAlpha (alpha);
1162- canvas.DrawTextFrame (frame, text_position , text_paint);
1166+ text_paint.color = Color::Yellow ().WithAlpha (options. alpha );
1167+ canvas.DrawTextFrame (frame, options. position , text_paint);
11631168 return true ;
11641169}
11651170
@@ -1171,6 +1176,49 @@ TEST_P(AiksTest, CanRenderTextFrame) {
11711176 ASSERT_TRUE (OpenPlaygroundHere (canvas.EndRecordingAsPicture ()));
11721177}
11731178
1179+ TEST_P (AiksTest, TextFrameSubpixelAlignment) {
1180+ std::array<Scalar, 20 > phase_offsets;
1181+ for (Scalar& offset : phase_offsets) {
1182+ offset = (static_cast <float >(arc4random ()) / static_cast <float >(RAND_MAX)) *
1183+ k2Pi;
1184+ }
1185+
1186+ auto callback = [&](AiksContext& renderer,
1187+ RenderTarget& render_target) -> bool {
1188+ static float font_size = 20 ;
1189+ static float phase_variation = 0.2 ;
1190+ static float speed = 0.5 ;
1191+ static float magnitude = 100 ;
1192+ ImGui::Begin (" Controls" , nullptr , ImGuiWindowFlags_AlwaysAutoResize);
1193+ ImGui::SliderFloat (" Font size" , &font_size, 5 , 50 );
1194+ ImGui::SliderFloat (" Phase variation" , &phase_variation, 0 , 1 );
1195+ ImGui::SliderFloat (" Oscillation speed" , &speed, 0 , 2 );
1196+ ImGui::SliderFloat (" Oscillation magnitude" , &magnitude, 0 , 300 );
1197+ ImGui::End ();
1198+
1199+ Canvas canvas;
1200+ canvas.Scale (GetContentScale ());
1201+
1202+ for (size_t i = 0 ; i < phase_offsets.size (); i++) {
1203+ auto position = Point (
1204+ 200 + magnitude * std::sin ((-phase_offsets[i] * phase_variation +
1205+ GetSecondsElapsed () * speed)), //
1206+ 200 + i * font_size * 1.1 //
1207+ );
1208+ if (!RenderTextInCanvas (GetContext (), canvas,
1209+ " the quick brown fox jumped over "
1210+ " the lazy dog!.?" ,
1211+ " Roboto-Regular.ttf" ,
1212+ {.font_size = font_size, .position = position})) {
1213+ return false ;
1214+ }
1215+ }
1216+ return renderer.Render (canvas.EndRecordingAsPicture (), render_target);
1217+ };
1218+
1219+ ASSERT_TRUE (OpenPlaygroundHere (callback));
1220+ }
1221+
11741222TEST_P (AiksTest, CanRenderItalicizedText) {
11751223 Canvas canvas;
11761224 ASSERT_TRUE (RenderTextInCanvas (
@@ -1196,10 +1244,11 @@ TEST_P(AiksTest, CanRenderEmojiTextFrameWithAlpha) {
11961244 ASSERT_TRUE (RenderTextInCanvas (GetContext (), canvas,
11971245 " π π π π π π
π π€£ π₯² π" ,
11981246#if FML_OS_MACOSX
1199- " Apple Color Emoji.ttc" , 50 , 0.5 ));
1247+ " Apple Color Emoji.ttc" , { . alpha = 0.5 }
12001248#else
1201- " NotoColorEmoji.ttf" , 50 , 0.5 ));
1249+ " NotoColorEmoji.ttf" , {. alpha = 0.5 }
12021250#endif
1251+ ));
12031252 ASSERT_TRUE (OpenPlaygroundHere (canvas.EndRecordingAsPicture ()));
12041253}
12051254
0 commit comments