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

Commit fe2ea5b

Browse files
committed
[Impeller] Round glyph locations per-run to fix per-glyph jitter
1 parent 96f0f5a commit fe2ea5b

File tree

2 files changed

+64
-14
lines changed

2 files changed

+64
-14
lines changed

β€Žimpeller/aiks/aiks_unittests.cc

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
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"
@@ -29,6 +31,7 @@
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

3437
namespace 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+
11291138
bool 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>(std::rand()) / static_cast<float>(RAND_MAX)) *
1183+
k2Pi; // NOLINT
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+
11741222
TEST_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

β€Žimpeller/typographer/backends/skia/text_frame_skia.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob, Scalar scale) {
7575
? Glyph::Type::kBitmap
7676
: Glyph::Type::kPath;
7777

78-
text_run.AddGlyph(Glyph{glyphs[i], type, ToRect(glyph_bounds[i])},
79-
Point{point->x(), point->y()});
78+
text_run.AddGlyph(
79+
Glyph{glyphs[i], type, ToRect(glyph_bounds[i])},
80+
Point{std::round(point->x()), std::round(point->y())});
8081
}
8182
break;
8283
}

0 commit comments

Comments
Β (0)