mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Round glyph locations per-run to fix per-glyph jitter (flutter/engine#40073)
This commit is contained in:
parent
76e2fd3b6b
commit
7d9b941133
@ -4,10 +4,12 @@
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "flutter/testing/testing.h"
|
||||
#include "impeller/aiks/aiks_playground.h"
|
||||
@ -29,6 +31,7 @@
|
||||
#include "impeller/scene/node.h"
|
||||
#include "impeller/typographer/backends/skia/text_frame_skia.h"
|
||||
#include "impeller/typographer/backends/skia/text_render_context_skia.h"
|
||||
#include "third_party/imgui/imgui.h"
|
||||
#include "third_party/skia/include/core/SkData.h"
|
||||
|
||||
namespace impeller {
|
||||
@ -1126,21 +1129,23 @@ static sk_sp<SkData> OpenFixtureAsSkData(const char* fixture_name) {
|
||||
return data;
|
||||
}
|
||||
|
||||
struct TextRenderOptions {
|
||||
Scalar font_size = 50;
|
||||
Scalar alpha = 1;
|
||||
Point position = Vector2(100, 200);
|
||||
};
|
||||
|
||||
bool RenderTextInCanvas(const std::shared_ptr<Context>& context,
|
||||
Canvas& canvas,
|
||||
const std::string& text,
|
||||
const std::string& font_fixture,
|
||||
Scalar font_size = 50.0,
|
||||
Scalar alpha = 1.0) {
|
||||
Scalar baseline = 200.0;
|
||||
Point text_position = {100, baseline};
|
||||
|
||||
TextRenderOptions options = {}) {
|
||||
// Draw the baseline.
|
||||
canvas.DrawRect({50, baseline, 900, 10},
|
||||
canvas.DrawRect({options.position.x - 50, options.position.y, 900, 10},
|
||||
Paint{.color = Color::Aqua().WithAlpha(0.25)});
|
||||
|
||||
// Mark the point at which the text is drawn.
|
||||
canvas.DrawCircle(text_position, 5.0,
|
||||
canvas.DrawCircle(options.position, 5.0,
|
||||
Paint{.color = Color::Red().WithAlpha(0.25)});
|
||||
|
||||
// Construct the text blob.
|
||||
@ -1148,7 +1153,7 @@ bool RenderTextInCanvas(const std::shared_ptr<Context>& context,
|
||||
if (!mapping) {
|
||||
return false;
|
||||
}
|
||||
SkFont sk_font(SkTypeface::MakeFromData(mapping), 50.0);
|
||||
SkFont sk_font(SkTypeface::MakeFromData(mapping), options.font_size);
|
||||
auto blob = SkTextBlob::MakeFromString(text.c_str(), sk_font);
|
||||
if (!blob) {
|
||||
return false;
|
||||
@ -1158,8 +1163,8 @@ bool RenderTextInCanvas(const std::shared_ptr<Context>& context,
|
||||
auto frame = TextFrameFromTextBlob(blob);
|
||||
|
||||
Paint text_paint;
|
||||
text_paint.color = Color::Yellow().WithAlpha(alpha);
|
||||
canvas.DrawTextFrame(frame, text_position, text_paint);
|
||||
text_paint.color = Color::Yellow().WithAlpha(options.alpha);
|
||||
canvas.DrawTextFrame(frame, options.position, text_paint);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1171,6 +1176,49 @@ TEST_P(AiksTest, CanRenderTextFrame) {
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, TextFrameSubpixelAlignment) {
|
||||
std::array<Scalar, 20> phase_offsets;
|
||||
for (Scalar& offset : phase_offsets) {
|
||||
auto rand = std::rand(); // NOLINT
|
||||
offset = (static_cast<float>(rand) / static_cast<float>(RAND_MAX)) * k2Pi;
|
||||
}
|
||||
|
||||
auto callback = [&](AiksContext& renderer,
|
||||
RenderTarget& render_target) -> bool {
|
||||
static float font_size = 20;
|
||||
static float phase_variation = 0.2;
|
||||
static float speed = 0.5;
|
||||
static float magnitude = 100;
|
||||
ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
|
||||
ImGui::SliderFloat("Font size", &font_size, 5, 50);
|
||||
ImGui::SliderFloat("Phase variation", &phase_variation, 0, 1);
|
||||
ImGui::SliderFloat("Oscillation speed", &speed, 0, 2);
|
||||
ImGui::SliderFloat("Oscillation magnitude", &magnitude, 0, 300);
|
||||
ImGui::End();
|
||||
|
||||
Canvas canvas;
|
||||
canvas.Scale(GetContentScale());
|
||||
|
||||
for (size_t i = 0; i < phase_offsets.size(); i++) {
|
||||
auto position = Point(
|
||||
200 + magnitude * std::sin((-phase_offsets[i] * phase_variation +
|
||||
GetSecondsElapsed() * speed)), //
|
||||
200 + i * font_size * 1.1 //
|
||||
);
|
||||
if (!RenderTextInCanvas(GetContext(), canvas,
|
||||
"the quick brown fox jumped over "
|
||||
"the lazy dog!.?",
|
||||
"Roboto-Regular.ttf",
|
||||
{.font_size = font_size, .position = position})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return renderer.Render(canvas.EndRecordingAsPicture(), render_target);
|
||||
};
|
||||
|
||||
ASSERT_TRUE(OpenPlaygroundHere(callback));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, CanRenderItalicizedText) {
|
||||
Canvas canvas;
|
||||
ASSERT_TRUE(RenderTextInCanvas(
|
||||
@ -1196,10 +1244,11 @@ TEST_P(AiksTest, CanRenderEmojiTextFrameWithAlpha) {
|
||||
ASSERT_TRUE(RenderTextInCanvas(GetContext(), canvas,
|
||||
"😀 😃 😄 😁 😆 😅 😂 🤣 🥲 😊",
|
||||
#if FML_OS_MACOSX
|
||||
"Apple Color Emoji.ttc", 50, 0.5));
|
||||
"Apple Color Emoji.ttc", { .alpha = 0.5 }
|
||||
#else
|
||||
"NotoColorEmoji.ttf", 50, 0.5));
|
||||
"NotoColorEmoji.ttf", {.alpha = 0.5}
|
||||
#endif
|
||||
));
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
|
||||
@ -75,8 +75,9 @@ TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob, Scalar scale) {
|
||||
? Glyph::Type::kBitmap
|
||||
: Glyph::Type::kPath;
|
||||
|
||||
text_run.AddGlyph(Glyph{glyphs[i], type, ToRect(glyph_bounds[i])},
|
||||
Point{point->x(), point->y()});
|
||||
text_run.AddGlyph(
|
||||
Glyph{glyphs[i], type, ToRect(glyph_bounds[i])},
|
||||
Point{std::round(point->x()), std::round(point->y())});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user