mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Improve resolution of text scaling. (flutter/engine#43533)
This patch does a few things: - Changes ownership of the LazyGlyphAtlas to ContentContext from Canvas. This means that drawings with multiple canvases (e.g. calls to DrawPicture) share the same lazy atlas. - Moves the scale property from Font::Metrics to FontGlyphPair. Font::Metrics contains properties related to the font, whereas scale has to do with the properties of the drawing at render time and is independent of the font. - Makes the lazy atlas manage FontGlyphPair::Set rather than vectors of TextFrames. - Makes the determination of font scaling at EntityPass::Render rather than Canvas::DrawTextFrame. The scaling may be altered by calls to Canvas::DrawPicture by render time and otherwise get missed. This is done via a new method: Contents::PopulateGlyphAtlas, which is only implemented by TextContents in this patch. - Fixes up some miscelleaneous bugs in Font::Metrics hashing and unused/dead code. This improves over prior attempts: LazyGlyphAtlas ownership is now unambiguous, and glyph scaling happens correctly for all rendered glyphs regardless of the order of canvas operations in Aiks. Fixes flutter/flutter#130142
This commit is contained in:
parent
e939f38a85
commit
21bb71b627
@ -2851,6 +2851,35 @@ TEST_P(AiksTest, CanCanvasDrawPicture) {
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, DrawPictureWithText) {
|
||||
Canvas subcanvas;
|
||||
ASSERT_TRUE(RenderTextInCanvas(
|
||||
GetContext(), subcanvas,
|
||||
"the quick brown fox jumped over the lazy dog!.?", "Roboto-Regular.ttf"));
|
||||
subcanvas.Translate({0, 10});
|
||||
subcanvas.Scale(Vector2(3, 3));
|
||||
ASSERT_TRUE(RenderTextInCanvas(
|
||||
GetContext(), subcanvas,
|
||||
"the quick brown fox jumped over the very big lazy dog!.?",
|
||||
"Roboto-Regular.ttf"));
|
||||
auto picture = subcanvas.EndRecordingAsPicture();
|
||||
|
||||
Canvas canvas;
|
||||
canvas.Scale(Vector2(.2, .2));
|
||||
canvas.Save();
|
||||
canvas.Translate({200, 200});
|
||||
canvas.Scale(Vector2(3.5, 3.5)); // The text must not be blurry after this.
|
||||
canvas.DrawPicture(picture);
|
||||
canvas.Restore();
|
||||
|
||||
canvas.Scale(Vector2(1.5, 1.5));
|
||||
ASSERT_TRUE(RenderTextInCanvas(
|
||||
GetContext(), canvas,
|
||||
"the quick brown fox jumped over the smaller lazy dog!.?",
|
||||
"Roboto-Regular.ttf"));
|
||||
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
|
||||
}
|
||||
|
||||
TEST_P(AiksTest, MatrixBackdropFilter) {
|
||||
Canvas canvas;
|
||||
canvas.SaveLayer({}, std::nullopt,
|
||||
|
||||
@ -42,7 +42,6 @@ void Canvas::Initialize(std::optional<Rect> cull_rect) {
|
||||
base_pass_ = std::make_unique<EntityPass>();
|
||||
current_pass_ = base_pass_.get();
|
||||
xformation_stack_.emplace_back(CanvasStackEntry{.cull_rect = cull_rect});
|
||||
lazy_glyph_atlas_ = std::make_shared<LazyGlyphAtlas>();
|
||||
FML_DCHECK(GetSaveCount() == 1u);
|
||||
FML_DCHECK(base_pass_->GetSubpassesDepth() == 1u);
|
||||
}
|
||||
@ -51,7 +50,6 @@ void Canvas::Reset() {
|
||||
base_pass_ = nullptr;
|
||||
current_pass_ = nullptr;
|
||||
xformation_stack_ = {};
|
||||
lazy_glyph_atlas_ = nullptr;
|
||||
}
|
||||
|
||||
void Canvas::Save() {
|
||||
@ -516,15 +514,12 @@ void Canvas::SaveLayer(const Paint& paint,
|
||||
void Canvas::DrawTextFrame(const TextFrame& text_frame,
|
||||
Point position,
|
||||
const Paint& paint) {
|
||||
lazy_glyph_atlas_->AddTextFrame(text_frame);
|
||||
|
||||
Entity entity;
|
||||
entity.SetStencilDepth(GetStencilDepth());
|
||||
entity.SetBlendMode(paint.blend_mode);
|
||||
|
||||
auto text_contents = std::make_shared<TextContents>();
|
||||
text_contents->SetTextFrame(text_frame);
|
||||
text_contents->SetGlyphAtlas(lazy_glyph_atlas_);
|
||||
|
||||
if (paint.color_source.GetType() != ColorSource::Type::kColor) {
|
||||
auto color_text_contents = std::make_shared<ColorSourceTextContents>();
|
||||
|
||||
@ -162,7 +162,6 @@ class Canvas {
|
||||
std::unique_ptr<EntityPass> base_pass_;
|
||||
EntityPass* current_pass_ = nullptr;
|
||||
std::deque<CanvasStackEntry> xformation_stack_;
|
||||
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
|
||||
std::optional<Rect> initial_cull_rect_;
|
||||
|
||||
void Initialize(std::optional<Rect> cull_rect);
|
||||
|
||||
@ -1104,10 +1104,9 @@ void DlDispatcher::drawDisplayList(
|
||||
void DlDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
|
||||
SkScalar x,
|
||||
SkScalar y) {
|
||||
Scalar scale = canvas_.GetCurrentTransformation().GetMaxBasisLengthXY();
|
||||
canvas_.DrawTextFrame(TextFrameFromTextBlob(blob, scale), //
|
||||
impeller::Point{x, y}, //
|
||||
paint_ //
|
||||
canvas_.DrawTextFrame(TextFrameFromTextBlob(blob), //
|
||||
impeller::Point{x, y}, //
|
||||
paint_ //
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "impeller/entity/contents/color_source_text_contents.h"
|
||||
|
||||
#include "color_source_text_contents.h"
|
||||
#include "impeller/entity/contents/content_context.h"
|
||||
#include "impeller/entity/contents/texture_contents.h"
|
||||
#include "impeller/renderer/render_pass.h"
|
||||
@ -33,6 +34,12 @@ void ColorSourceTextContents::SetTextPosition(Point position) {
|
||||
position_ = position;
|
||||
}
|
||||
|
||||
void ColorSourceTextContents::PopulateGlyphAtlas(
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
|
||||
Scalar scale) const {
|
||||
text_contents_->PopulateGlyphAtlas(lazy_glyph_atlas, scale);
|
||||
}
|
||||
|
||||
bool ColorSourceTextContents::Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
|
||||
@ -32,6 +32,11 @@ class ColorSourceTextContents final : public Contents {
|
||||
// |Contents|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
|
||||
// |Contents|
|
||||
void PopulateGlyphAtlas(
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
|
||||
Scalar scale) const override;
|
||||
|
||||
// |Contents|
|
||||
bool Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
|
||||
@ -696,8 +696,18 @@ class ContentContext {
|
||||
const SubpassCallback& subpass_callback,
|
||||
bool msaa_enabled = true) const;
|
||||
|
||||
void SetLazyGlyphAtlas(
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas) {
|
||||
lazy_glyph_atlas_ = lazy_glyph_atlas;
|
||||
}
|
||||
|
||||
std::shared_ptr<LazyGlyphAtlas> GetLazyGlyphAtlas() const {
|
||||
return lazy_glyph_atlas_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Context> context_;
|
||||
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
|
||||
|
||||
template <class T>
|
||||
using Variants = std::unordered_map<ContentContextOptions,
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#include "impeller/geometry/color.h"
|
||||
#include "impeller/geometry/rect.h"
|
||||
#include "impeller/renderer/snapshot.h"
|
||||
#include "impeller/typographer/lazy_glyph_atlas.h"
|
||||
|
||||
namespace impeller {
|
||||
|
||||
@ -49,6 +50,11 @@ class Contents {
|
||||
|
||||
virtual ~Contents();
|
||||
|
||||
/// @brief Add any text data to the specified lazy atlas.
|
||||
virtual void PopulateGlyphAtlas(
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
|
||||
Scalar scale) const {}
|
||||
|
||||
virtual bool Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const = 0;
|
||||
|
||||
@ -29,18 +29,15 @@ void TextContents::SetTextFrame(const TextFrame& frame) {
|
||||
frame_ = frame;
|
||||
}
|
||||
|
||||
void TextContents::SetGlyphAtlas(std::shared_ptr<LazyGlyphAtlas> atlas) {
|
||||
lazy_atlas_ = std::move(atlas);
|
||||
}
|
||||
|
||||
std::shared_ptr<GlyphAtlas> TextContents::ResolveAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
std::shared_ptr<Context> context) const {
|
||||
FML_DCHECK(lazy_atlas_);
|
||||
if (lazy_atlas_) {
|
||||
return lazy_atlas_->CreateOrGetGlyphAtlas(type, std::move(atlas_context),
|
||||
std::move(context));
|
||||
FML_DCHECK(lazy_atlas);
|
||||
if (lazy_atlas) {
|
||||
return lazy_atlas->CreateOrGetGlyphAtlas(type, std::move(atlas_context),
|
||||
std::move(context));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -78,14 +75,43 @@ std::optional<Rect> TextContents::GetCoverage(const Entity& entity) const {
|
||||
return bounds->TransformBounds(entity.GetTransformation());
|
||||
}
|
||||
|
||||
static bool CommonRender(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass,
|
||||
const Color& color,
|
||||
const TextFrame& frame,
|
||||
Vector2 offset,
|
||||
const std::shared_ptr<GlyphAtlas>& atlas,
|
||||
Command& cmd) {
|
||||
void TextContents::PopulateGlyphAtlas(
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
|
||||
Scalar scale) const {
|
||||
lazy_glyph_atlas->AddTextFrame(frame_, scale);
|
||||
}
|
||||
|
||||
bool TextContents::Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
auto color = GetColor();
|
||||
if (color.IsTransparent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto type = frame_.GetAtlasType();
|
||||
auto scale = entity.DeriveTextScale();
|
||||
auto atlas =
|
||||
ResolveAtlas(type, renderer.GetLazyGlyphAtlas(),
|
||||
renderer.GetGlyphAtlasContext(type), renderer.GetContext());
|
||||
|
||||
if (!atlas || !atlas->IsValid()) {
|
||||
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Information shared by all glyph draw calls.
|
||||
Command cmd;
|
||||
cmd.label = "TextFrame";
|
||||
auto opts = OptionsFromPassAndEntity(pass, entity);
|
||||
opts.primitive_type = PrimitiveType::kTriangle;
|
||||
if (type == GlyphAtlas::Type::kAlphaBitmap) {
|
||||
cmd.pipeline = renderer.GetGlyphAtlasPipeline(opts);
|
||||
} else {
|
||||
cmd.pipeline = renderer.GetGlyphAtlasColorPipeline(opts);
|
||||
}
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
|
||||
using VS = GlyphAtlasPipeline::VertexShader;
|
||||
using FS = GlyphAtlasPipeline::FragmentShader;
|
||||
|
||||
@ -95,7 +121,7 @@ static bool CommonRender(const ContentContext& renderer,
|
||||
frame_info.atlas_size =
|
||||
Vector2{static_cast<Scalar>(atlas->GetTexture()->GetSize().width),
|
||||
static_cast<Scalar>(atlas->GetTexture()->GetSize().height)};
|
||||
frame_info.offset = offset;
|
||||
frame_info.offset = offset_;
|
||||
frame_info.is_translation_scale =
|
||||
entity.GetTransformation().IsTranslationScaleOnly();
|
||||
frame_info.entity_transform = entity.GetTransformation();
|
||||
@ -141,7 +167,7 @@ static bool CommonRender(const ContentContext& renderer,
|
||||
|
||||
auto& host_buffer = pass.GetTransientsBuffer();
|
||||
size_t vertex_count = 0;
|
||||
for (const auto& run : frame.GetRuns()) {
|
||||
for (const auto& run : frame_.GetRuns()) {
|
||||
vertex_count += run.GetGlyphPositions().size();
|
||||
}
|
||||
vertex_count *= 6;
|
||||
@ -151,10 +177,10 @@ static bool CommonRender(const ContentContext& renderer,
|
||||
[&](uint8_t* contents) {
|
||||
VS::PerVertexData vtx;
|
||||
size_t vertex_offset = 0;
|
||||
for (const auto& run : frame.GetRuns()) {
|
||||
for (const auto& run : frame_.GetRuns()) {
|
||||
const Font& font = run.GetFont();
|
||||
for (const auto& glyph_position : run.GetGlyphPositions()) {
|
||||
FontGlyphPair font_glyph_pair{font, glyph_position.glyph};
|
||||
FontGlyphPair font_glyph_pair{font, glyph_position.glyph, scale};
|
||||
auto maybe_atlas_glyph_bounds =
|
||||
atlas->FindFontGlyphBounds(font_glyph_pair);
|
||||
if (!maybe_atlas_glyph_bounds.has_value()) {
|
||||
@ -191,37 +217,4 @@ static bool CommonRender(const ContentContext& renderer,
|
||||
return pass.AddCommand(cmd);
|
||||
}
|
||||
|
||||
bool TextContents::Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
RenderPass& pass) const {
|
||||
auto color = GetColor();
|
||||
if (color.IsTransparent()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto type = frame_.GetAtlasType();
|
||||
auto atlas = ResolveAtlas(type, renderer.GetGlyphAtlasContext(type),
|
||||
renderer.GetContext());
|
||||
|
||||
if (!atlas || !atlas->IsValid()) {
|
||||
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Information shared by all glyph draw calls.
|
||||
Command cmd;
|
||||
cmd.label = "TextFrame";
|
||||
auto opts = OptionsFromPassAndEntity(pass, entity);
|
||||
opts.primitive_type = PrimitiveType::kTriangle;
|
||||
if (type == GlyphAtlas::Type::kAlphaBitmap) {
|
||||
cmd.pipeline = renderer.GetGlyphAtlasPipeline(opts);
|
||||
} else {
|
||||
cmd.pipeline = renderer.GetGlyphAtlasColorPipeline(opts);
|
||||
}
|
||||
cmd.stencil_reference = entity.GetStencilDepth();
|
||||
|
||||
return CommonRender(renderer, entity, pass, color, frame_, offset_, atlas,
|
||||
cmd);
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -28,14 +28,14 @@ class TextContents final : public Contents {
|
||||
|
||||
void SetTextFrame(const TextFrame& frame);
|
||||
|
||||
void SetGlyphAtlas(std::shared_ptr<LazyGlyphAtlas> atlas);
|
||||
|
||||
void SetColor(Color color);
|
||||
|
||||
Color GetColor() const;
|
||||
|
||||
// |Contents|
|
||||
bool CanInheritOpacity(const Entity& entity) const override;
|
||||
|
||||
// |Contents|
|
||||
void SetInheritedOpacity(Scalar opacity) override;
|
||||
|
||||
void SetOffset(Vector2 offset);
|
||||
@ -45,6 +45,11 @@ class TextContents final : public Contents {
|
||||
// |Contents|
|
||||
std::optional<Rect> GetCoverage(const Entity& entity) const override;
|
||||
|
||||
// |Contents|
|
||||
void PopulateGlyphAtlas(
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
|
||||
Scalar scale) const override;
|
||||
|
||||
// |Contents|
|
||||
bool Render(const ContentContext& renderer,
|
||||
const Entity& entity,
|
||||
@ -54,11 +59,11 @@ class TextContents final : public Contents {
|
||||
TextFrame frame_;
|
||||
Color color_;
|
||||
Scalar inherited_opacity_ = 1.0;
|
||||
mutable std::shared_ptr<LazyGlyphAtlas> lazy_atlas_;
|
||||
Vector2 offset_;
|
||||
|
||||
std::shared_ptr<GlyphAtlas> ResolveAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
std::shared_ptr<Context> context) const;
|
||||
|
||||
|
||||
@ -166,4 +166,8 @@ bool Entity::Render(const ContentContext& renderer,
|
||||
return contents_->Render(renderer, *this, parent_pass);
|
||||
}
|
||||
|
||||
Scalar Entity::DeriveTextScale() const {
|
||||
return GetTransformation().GetMaxBasisLengthXY();
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -94,6 +94,8 @@ class Entity {
|
||||
|
||||
std::optional<Color> AsBackgroundColor(ISize target_size) const;
|
||||
|
||||
Scalar DeriveTextScale() const;
|
||||
|
||||
private:
|
||||
Matrix transformation_;
|
||||
std::shared_ptr<Contents> contents_;
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "flutter/fml/closure.h"
|
||||
#include "flutter/fml/logging.h"
|
||||
#include "flutter/fml/macros.h"
|
||||
#include "flutter/fml/trace_event.h"
|
||||
@ -252,6 +253,18 @@ bool EntityPass::Render(ContentContext& renderer,
|
||||
return false;
|
||||
}
|
||||
|
||||
renderer.SetLazyGlyphAtlas(std::make_shared<LazyGlyphAtlas>());
|
||||
fml::ScopedCleanupClosure reset_lazy_glyph_atlas(
|
||||
[&renderer]() { renderer.SetLazyGlyphAtlas(nullptr); });
|
||||
|
||||
IterateAllEntities([lazy_glyph_atlas =
|
||||
renderer.GetLazyGlyphAtlas()](const Entity& entity) {
|
||||
if (auto contents = entity.GetContents()) {
|
||||
contents->PopulateGlyphAtlas(lazy_glyph_atlas, entity.DeriveTextScale());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
StencilCoverageStack stencil_coverage_stack = {StencilCoverageLayer{
|
||||
.coverage = Rect::MakeSize(root_render_target.GetRenderTargetSize()),
|
||||
.stencil_depth = 0}};
|
||||
@ -878,6 +891,28 @@ void EntityPass::IterateAllEntities(
|
||||
}
|
||||
}
|
||||
|
||||
void EntityPass::IterateAllEntities(
|
||||
const std::function<bool(const Entity&)>& iterator) const {
|
||||
if (!iterator) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& element : elements_) {
|
||||
if (auto entity = std::get_if<Entity>(&element)) {
|
||||
if (!iterator(*entity)) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
|
||||
const EntityPass* entity_pass = subpass->get();
|
||||
entity_pass->IterateAllEntities(iterator);
|
||||
continue;
|
||||
}
|
||||
FML_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityPass::IterateUntilSubpass(
|
||||
const std::function<bool(Entity&)>& iterator) {
|
||||
if (!iterator) {
|
||||
|
||||
@ -79,6 +79,12 @@ class EntityPass {
|
||||
/// of child passes. The iteration order is depth-first.
|
||||
void IterateAllEntities(const std::function<bool(Entity&)>& iterator);
|
||||
|
||||
/// @brief Iterate all entities in this pass, recursively including entities
|
||||
/// of child passes. The iteration order is depth-first and does not
|
||||
/// allow modification of the entities.
|
||||
void IterateAllEntities(
|
||||
const std::function<bool(const Entity&)>& iterator) const;
|
||||
|
||||
/// @brief Iterate entities in this pass up until the first subpass is found.
|
||||
/// This is useful for limiting look-ahead optimizations.
|
||||
///
|
||||
|
||||
@ -2184,7 +2184,7 @@ TEST_P(EntityTest, InheritOpacityTest) {
|
||||
auto blob = SkTextBlob::MakeFromString("A", font);
|
||||
auto frame = TextFrameFromTextBlob(blob);
|
||||
auto lazy_glyph_atlas = std::make_shared<LazyGlyphAtlas>();
|
||||
lazy_glyph_atlas->AddTextFrame(frame);
|
||||
lazy_glyph_atlas->AddTextFrame(frame, 1.0f);
|
||||
|
||||
auto text_contents = std::make_shared<TextContents>();
|
||||
text_contents->SetTextFrame(frame);
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
namespace impeller {
|
||||
|
||||
static Font ToFont(const SkTextBlobRunIterator& run, Scalar scale) {
|
||||
static Font ToFont(const SkTextBlobRunIterator& run) {
|
||||
auto& font = run.font();
|
||||
auto typeface = std::make_shared<TypefaceSkia>(font.refTypefaceOrDefault());
|
||||
|
||||
@ -25,7 +25,6 @@ static Font ToFont(const SkTextBlobRunIterator& run, Scalar scale) {
|
||||
font.getMetrics(&sk_metrics);
|
||||
|
||||
Font::Metrics metrics;
|
||||
metrics.scale = scale;
|
||||
metrics.point_size = font.getSize();
|
||||
metrics.embolden = font.isEmbolden();
|
||||
metrics.skewX = font.getSkewX();
|
||||
@ -38,7 +37,7 @@ static Rect ToRect(const SkRect& rect) {
|
||||
return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
|
||||
}
|
||||
|
||||
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob, Scalar scale) {
|
||||
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob) {
|
||||
if (!blob) {
|
||||
return {};
|
||||
}
|
||||
@ -46,7 +45,7 @@ TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob, Scalar scale) {
|
||||
TextFrame frame;
|
||||
|
||||
for (SkTextBlobRunIterator run(blob.get()); !run.done(); run.next()) {
|
||||
TextRun text_run(ToFont(run, scale));
|
||||
TextRun text_run(ToFont(run));
|
||||
|
||||
// TODO(jonahwilliams): ask Skia for a public API to look this up.
|
||||
// https://github.com/flutter/flutter/issues/112005
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
|
||||
namespace impeller {
|
||||
|
||||
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob,
|
||||
Scalar scale = 1.0f);
|
||||
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob);
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -40,23 +40,6 @@ TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr<Context> context)
|
||||
|
||||
TextRenderContextSkia::~TextRenderContextSkia() = default;
|
||||
|
||||
static FontGlyphPair::Set CollectUniqueFontGlyphPairs(
|
||||
GlyphAtlas::Type type,
|
||||
const TextRenderContext::FrameIterator& frame_iterator) {
|
||||
TRACE_EVENT0("impeller", __FUNCTION__);
|
||||
FontGlyphPair::Set set;
|
||||
while (const TextFrame* frame = frame_iterator()) {
|
||||
for (const TextRun& run : frame->GetRuns()) {
|
||||
const Font& font = run.GetFont();
|
||||
for (const TextRun::GlyphPosition& glyph_position :
|
||||
run.GetGlyphPositions()) {
|
||||
set.insert({font, glyph_position.glyph});
|
||||
}
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
static size_t PairsFitInAtlasOfSize(
|
||||
const FontGlyphPair::Set& pairs,
|
||||
const ISize& atlas_size,
|
||||
@ -73,8 +56,7 @@ static size_t PairsFitInAtlasOfSize(
|
||||
for (auto it = pairs.begin(); it != pairs.end(); ++i, ++it) {
|
||||
const auto& pair = *it;
|
||||
|
||||
const auto glyph_size =
|
||||
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
|
||||
const auto glyph_size = ISize::Ceil((pair.glyph.bounds * pair.scale).size);
|
||||
IPoint16 location_in_atlas;
|
||||
if (!rect_packer->addRect(glyph_size.width + kPadding, //
|
||||
glyph_size.height + kPadding, //
|
||||
@ -111,8 +93,7 @@ static bool CanAppendToExistingAtlas(
|
||||
for (size_t i = 0; i < extra_pairs.size(); i++) {
|
||||
const FontGlyphPair& pair = extra_pairs[i];
|
||||
|
||||
const auto glyph_size =
|
||||
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
|
||||
const auto glyph_size = ISize::Ceil((pair.glyph.bounds * pair.scale).size);
|
||||
IPoint16 location_in_atlas;
|
||||
if (!rect_packer->addRect(glyph_size.width + kPadding, //
|
||||
glyph_size.height + kPadding, //
|
||||
@ -176,8 +157,8 @@ static void DrawGlyph(SkCanvas* canvas,
|
||||
const Rect& location,
|
||||
bool has_color) {
|
||||
const auto& metrics = font_glyph.font.GetMetrics();
|
||||
const auto position = SkPoint::Make(location.origin.x / metrics.scale,
|
||||
location.origin.y / metrics.scale);
|
||||
const auto position = SkPoint::Make(location.origin.x / font_glyph.scale,
|
||||
location.origin.y / font_glyph.scale);
|
||||
SkGlyphID glyph_id = font_glyph.glyph.index;
|
||||
|
||||
SkFont sk_font(
|
||||
@ -192,7 +173,7 @@ static void DrawGlyph(SkCanvas* canvas,
|
||||
SkPaint glyph_paint;
|
||||
glyph_paint.setColor(glyph_color);
|
||||
canvas->resetMatrix();
|
||||
canvas->scale(metrics.scale, metrics.scale);
|
||||
canvas->scale(font_glyph.scale, font_glyph.scale);
|
||||
canvas->drawGlyphs(
|
||||
1u, // count
|
||||
&glyph_id, // glyphs
|
||||
@ -331,25 +312,19 @@ static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
|
||||
std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
FrameIterator frame_iterator) const {
|
||||
const FontGlyphPair::Set& font_glyph_pairs) const {
|
||||
TRACE_EVENT0("impeller", __FUNCTION__);
|
||||
if (!IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 1: Collect unique font-glyph pairs in the frame.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
FontGlyphPair::Set font_glyph_pairs =
|
||||
CollectUniqueFontGlyphPairs(type, frame_iterator);
|
||||
if (font_glyph_pairs.empty()) {
|
||||
return last_atlas;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 2: Determine if the atlas type and font glyph pairs are compatible
|
||||
// Step 1: Determine if the atlas type and font glyph pairs are compatible
|
||||
// with the current atlas and reuse if possible.
|
||||
// ---------------------------------------------------------------------------
|
||||
FontGlyphPairRefVector new_glyphs;
|
||||
@ -363,7 +338,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 3: Determine if the additional missing glyphs can be appended to the
|
||||
// Step 2: Determine if the additional missing glyphs can be appended to the
|
||||
// existing bitmap without recreating the atlas. This requires that
|
||||
// the type is identical.
|
||||
// ---------------------------------------------------------------------------
|
||||
@ -376,7 +351,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
// added.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 4: Record the positions in the glyph atlas of the newly added
|
||||
// Step 3a: Record the positions in the glyph atlas of the newly added
|
||||
// glyphs.
|
||||
// ---------------------------------------------------------------------------
|
||||
for (size_t i = 0, count = glyph_positions.size(); i < count; i++) {
|
||||
@ -384,7 +359,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 5: Draw new font-glyph pairs into the existing bitmap.
|
||||
// Step 4a: Draw new font-glyph pairs into the existing bitmap.
|
||||
// ---------------------------------------------------------------------------
|
||||
auto bitmap = atlas_context->GetBitmap();
|
||||
if (!UpdateAtlasBitmap(*last_atlas, bitmap, new_glyphs)) {
|
||||
@ -392,7 +367,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 6: Update the existing texture with the updated bitmap.
|
||||
// Step 5a: Update the existing texture with the updated bitmap.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) {
|
||||
return nullptr;
|
||||
@ -402,7 +377,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
// A new glyph atlas must be created.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 4: Get the optimum size of the texture atlas.
|
||||
// Step 3b: Get the optimum size of the texture atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
|
||||
auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
|
||||
@ -413,7 +388,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
return nullptr;
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 5: Find location of font-glyph pairs in the atlas. We have this from
|
||||
// Step 4b: Find location of font-glyph pairs in the atlas. We have this from
|
||||
// the last step. So no need to do create another rect packer. But just do a
|
||||
// sanity check of counts. This could also be just an assertion as only a
|
||||
// construction issue would cause such a failure.
|
||||
@ -423,7 +398,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 6: Record the positions in the glyph atlas.
|
||||
// Step 5b: Record the positions in the glyph atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
{
|
||||
size_t i = 0;
|
||||
@ -434,7 +409,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 7: Draw font-glyph pairs in the correct spot in the atlas.
|
||||
// Step 6b: Draw font-glyph pairs in the correct spot in the atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
|
||||
if (!bitmap) {
|
||||
@ -443,7 +418,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
atlas_context->UpdateBitmap(bitmap);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 8: Upload the atlas as a texture.
|
||||
// Step 7b: Upload the atlas as a texture.
|
||||
// ---------------------------------------------------------------------------
|
||||
PixelFormat format;
|
||||
switch (type) {
|
||||
@ -461,7 +436,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 9: Record the texture in the glyph atlas.
|
||||
// Step 8b: Record the texture in the glyph atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
glyph_atlas->SetTexture(std::move(texture));
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ class TextRenderContextSkia : public TextRenderContext {
|
||||
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
FrameIterator iterator) const override;
|
||||
const FontGlyphPair::Set& font_glyph_pairs) const override;
|
||||
|
||||
private:
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(TextRenderContextSkia);
|
||||
|
||||
@ -28,12 +28,6 @@ class Font : public Comparable<Font> {
|
||||
/// the baseline with an upper-left-origin coordinate system.
|
||||
///
|
||||
struct Metrics {
|
||||
//--------------------------------------------------------------------------
|
||||
/// The scaling factor that should be used when rendering this font to an
|
||||
/// atlas. This should normally be set in accordance with the transformation
|
||||
/// matrix that will be used to position glyph geometry.
|
||||
///
|
||||
Scalar scale = 1.0f;
|
||||
//--------------------------------------------------------------------------
|
||||
/// The point size of the font.
|
||||
///
|
||||
@ -43,8 +37,8 @@ class Font : public Comparable<Font> {
|
||||
Scalar scaleX = 1.0f;
|
||||
|
||||
constexpr bool operator==(const Metrics& o) const {
|
||||
return scale == o.scale && point_size == o.point_size &&
|
||||
embolden == o.embolden && skewX == o.skewX && scaleX == o.scaleX;
|
||||
return point_size == o.point_size && embolden == o.embolden &&
|
||||
skewX == o.skewX && scaleX == o.scaleX;
|
||||
}
|
||||
};
|
||||
|
||||
@ -80,6 +74,6 @@ class Font : public Comparable<Font> {
|
||||
template <>
|
||||
struct std::hash<impeller::Font::Metrics> {
|
||||
constexpr std::size_t operator()(const impeller::Font::Metrics& m) const {
|
||||
return fml::HashCombine(m.scale, m.point_size);
|
||||
return fml::HashCombine(m.point_size, m.skewX, m.scaleX);
|
||||
}
|
||||
};
|
||||
|
||||
@ -16,28 +16,29 @@
|
||||
namespace impeller {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/// @brief A font along with a glyph in that font. Used in glyph atlases as
|
||||
/// keys.
|
||||
/// @brief A font along with a glyph in that font rendered at a particular
|
||||
/// scale. Used in glyph atlases as keys.
|
||||
///
|
||||
struct FontGlyphPair {
|
||||
struct Hash;
|
||||
struct Equal;
|
||||
|
||||
using Set = std::unordered_set<FontGlyphPair, Hash, Equal>;
|
||||
using Vector = std::vector<FontGlyphPair>;
|
||||
|
||||
Font font;
|
||||
Glyph glyph;
|
||||
Scalar scale;
|
||||
|
||||
struct Hash {
|
||||
std::size_t operator()(const FontGlyphPair& p) const {
|
||||
return fml::HashCombine(p.font.GetHash(), p.glyph.index, p.glyph.type);
|
||||
return fml::HashCombine(p.font.GetHash(), p.glyph.index, p.glyph.type,
|
||||
p.scale);
|
||||
}
|
||||
};
|
||||
struct Equal {
|
||||
bool operator()(const FontGlyphPair& lhs, const FontGlyphPair& rhs) const {
|
||||
return lhs.font.IsEqual(rhs.font) && lhs.glyph.index == rhs.glyph.index &&
|
||||
lhs.glyph.type == rhs.glyph.type;
|
||||
lhs.glyph.type == rhs.glyph.type && lhs.scale == rhs.scale;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -15,12 +15,12 @@ LazyGlyphAtlas::LazyGlyphAtlas() = default;
|
||||
|
||||
LazyGlyphAtlas::~LazyGlyphAtlas() = default;
|
||||
|
||||
void LazyGlyphAtlas::AddTextFrame(const TextFrame& frame) {
|
||||
void LazyGlyphAtlas::AddTextFrame(const TextFrame& frame, Scalar scale) {
|
||||
FML_DCHECK(atlas_map_.empty());
|
||||
if (frame.GetAtlasType() == GlyphAtlas::Type::kAlphaBitmap) {
|
||||
alpha_frames_.emplace_back(frame);
|
||||
frame.CollectUniqueFontGlyphPairs(alpha_set_, scale);
|
||||
} else {
|
||||
color_frames_.emplace_back(frame);
|
||||
frame.CollectUniqueFontGlyphPairs(color_set_, scale);
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,19 +39,9 @@ std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
|
||||
if (!text_context || !text_context->IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t i = 0;
|
||||
auto frames =
|
||||
type == GlyphAtlas::Type::kAlphaBitmap ? alpha_frames_ : color_frames_;
|
||||
TextRenderContext::FrameIterator iterator = [&]() -> const TextFrame* {
|
||||
if (i >= frames.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto& result = frames[i];
|
||||
i++;
|
||||
return &result;
|
||||
};
|
||||
auto& set = type == GlyphAtlas::Type::kAlphaBitmap ? alpha_set_ : color_set_;
|
||||
auto atlas =
|
||||
text_context->CreateGlyphAtlas(type, std::move(atlas_context), iterator);
|
||||
text_context->CreateGlyphAtlas(type, std::move(atlas_context), set);
|
||||
if (!atlas || !atlas->IsValid()) {
|
||||
VALIDATION_LOG << "Could not create valid atlas.";
|
||||
return nullptr;
|
||||
|
||||
@ -19,7 +19,7 @@ class LazyGlyphAtlas {
|
||||
|
||||
~LazyGlyphAtlas();
|
||||
|
||||
void AddTextFrame(const TextFrame& frame);
|
||||
void AddTextFrame(const TextFrame& frame, Scalar scale);
|
||||
|
||||
std::shared_ptr<GlyphAtlas> CreateOrGetGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
@ -27,8 +27,8 @@ class LazyGlyphAtlas {
|
||||
std::shared_ptr<Context> context) const;
|
||||
|
||||
private:
|
||||
std::vector<TextFrame> alpha_frames_;
|
||||
std::vector<TextFrame> color_frames_;
|
||||
FontGlyphPair::Set alpha_set_;
|
||||
FontGlyphPair::Set color_set_;
|
||||
mutable std::unordered_map<GlyphAtlas::Type, std::shared_ptr<GlyphAtlas>>
|
||||
atlas_map_;
|
||||
|
||||
|
||||
@ -79,4 +79,15 @@ bool TextFrame::MaybeHasOverlapping() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TextFrame::CollectUniqueFontGlyphPairs(FontGlyphPair::Set& set,
|
||||
Scalar scale) const {
|
||||
for (const TextRun& run : GetRuns()) {
|
||||
const Font& font = run.GetFont();
|
||||
for (const TextRun::GlyphPosition& glyph_position :
|
||||
run.GetGlyphPositions()) {
|
||||
set.insert({font, glyph_position.glyph, scale});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -22,6 +22,8 @@ class TextFrame {
|
||||
|
||||
~TextFrame();
|
||||
|
||||
void CollectUniqueFontGlyphPairs(FontGlyphPair::Set& set, Scalar scale) const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief The conservative bounding box for this text frame.
|
||||
///
|
||||
|
||||
@ -26,19 +26,4 @@ const std::shared_ptr<Context>& TextRenderContext::GetContext() const {
|
||||
return context_;
|
||||
}
|
||||
|
||||
std::shared_ptr<GlyphAtlas> TextRenderContext::CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
const TextFrame& frame) const {
|
||||
size_t count = 0;
|
||||
FrameIterator iterator = [&]() -> const TextFrame* {
|
||||
count++;
|
||||
if (count == 1) {
|
||||
return &frame;
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
return CreateGlyphAtlas(type, std::move(atlas_context), iterator);
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -37,20 +37,13 @@ class TextRenderContext {
|
||||
///
|
||||
const std::shared_ptr<Context>& GetContext() const;
|
||||
|
||||
using FrameIterator = std::function<const TextFrame*(void)>;
|
||||
|
||||
// TODO(dnfield): Callers should not need to know which type of atlas to
|
||||
// create. https://github.com/flutter/flutter/issues/111640
|
||||
|
||||
virtual std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
FrameIterator iterator) const = 0;
|
||||
|
||||
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
|
||||
GlyphAtlas::Type type,
|
||||
std::shared_ptr<GlyphAtlasContext> atlas_context,
|
||||
const TextFrame& frame) const;
|
||||
const FontGlyphPair::Set& font_glyph_pairs) const = 0;
|
||||
|
||||
protected:
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
@ -23,6 +23,17 @@ namespace testing {
|
||||
using TypographerTest = PlaygroundTest;
|
||||
INSTANTIATE_PLAYGROUND_SUITE(TypographerTest);
|
||||
|
||||
static std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
|
||||
const TextRenderContext* context,
|
||||
GlyphAtlas::Type type,
|
||||
Scalar scale,
|
||||
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
|
||||
const TextFrame& frame) {
|
||||
FontGlyphPair::Set set;
|
||||
frame.CollectUniqueFontGlyphPairs(set, scale);
|
||||
return context->CreateGlyphAtlas(type, atlas_context, set);
|
||||
}
|
||||
|
||||
TEST_P(TypographerTest, CanConvertTextBlob) {
|
||||
SkFont font;
|
||||
auto blob = SkTextBlob::MakeFromString(
|
||||
@ -49,8 +60,8 @@ TEST_P(TypographerTest, CanCreateGlyphAtlas) {
|
||||
auto blob = SkTextBlob::MakeFromString("hello", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob));
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap);
|
||||
@ -102,13 +113,13 @@ TEST_P(TypographerTest, LazyAtlasTracksColor) {
|
||||
|
||||
LazyGlyphAtlas lazy_atlas;
|
||||
|
||||
lazy_atlas.AddTextFrame(frame);
|
||||
lazy_atlas.AddTextFrame(frame, 1.0f);
|
||||
|
||||
frame = TextFrameFromTextBlob(SkTextBlob::MakeFromString("😀 ", emoji_font));
|
||||
|
||||
ASSERT_TRUE(frame.GetAtlasType() == GlyphAtlas::Type::kColorBitmap);
|
||||
|
||||
lazy_atlas.AddTextFrame(frame);
|
||||
lazy_atlas.AddTextFrame(frame, 1.0f);
|
||||
|
||||
// Creates different atlases for color and alpha bitmap.
|
||||
auto color_context = std::make_shared<GlyphAtlasContext>();
|
||||
@ -130,8 +141,8 @@ TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
|
||||
auto blob = SkTextBlob::MakeFromString("AGH", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob));
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
|
||||
@ -147,8 +158,8 @@ TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
|
||||
auto blob = SkTextBlob::MakeFromString("spooky skellingtons", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob));
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
|
||||
@ -156,8 +167,8 @@ TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
|
||||
// now attempt to re-create an atlas with the same text blob.
|
||||
|
||||
auto next_atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob));
|
||||
ASSERT_EQ(atlas, next_atlas);
|
||||
ASSERT_EQ(atlas_context->GetGlyphAtlas(), atlas);
|
||||
}
|
||||
@ -166,7 +177,6 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
|
||||
auto context = TextRenderContext::Create(GetContext());
|
||||
auto atlas_context = std::make_shared<GlyphAtlasContext>();
|
||||
ASSERT_TRUE(context && context->IsValid());
|
||||
SkFont sk_font;
|
||||
|
||||
const char* test_string =
|
||||
"QWERTYUIOPASDFGHJKLZXCVBNMqewrtyuiopasdfghjklzxcvbnm,.<>[]{};':"
|
||||
@ -174,22 +184,17 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
|
||||
"œ∑´®†¥¨ˆøπ““‘‘åß∂ƒ©˙∆˚¬…æ≈ç√∫˜µ≤≥≥≥≥÷¡™£¢∞§¶•ªº–≠⁄€‹›fifl‡°·‚—±Œ„´‰Á¨Ø∏”’/"
|
||||
"* Í˝ */¸˛Ç◊ı˜Â¯˘¿";
|
||||
|
||||
SkFont sk_font;
|
||||
auto blob = SkTextBlob::MakeFromString(test_string, sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
|
||||
TextFrame frame;
|
||||
size_t count = 0;
|
||||
const int size_count = 8;
|
||||
TextRenderContext::FrameIterator iterator = [&]() -> const TextFrame* {
|
||||
if (count < size_count) {
|
||||
count++;
|
||||
frame = TextFrameFromTextBlob(blob, 0.6 * count);
|
||||
return &frame;
|
||||
}
|
||||
return nullptr;
|
||||
FontGlyphPair::Set set;
|
||||
size_t size_count = 8;
|
||||
for (size_t index = 0; index < size_count; index += 1) {
|
||||
TextFrameFromTextBlob(blob).CollectUniqueFontGlyphPairs(set, 0.6 * index);
|
||||
};
|
||||
auto atlas = context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap,
|
||||
atlas_context, iterator);
|
||||
std::move(atlas_context), set);
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
|
||||
@ -217,8 +222,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
|
||||
auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob));
|
||||
auto old_packer = atlas_context->GetRectPacker();
|
||||
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
@ -231,8 +236,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
|
||||
|
||||
auto blob2 = SkTextBlob::MakeFromString("spooky 2", sk_font);
|
||||
auto next_atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob2));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob2));
|
||||
ASSERT_EQ(atlas, next_atlas);
|
||||
auto* second_texture = next_atlas->GetTexture().get();
|
||||
|
||||
@ -250,8 +255,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
|
||||
auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
|
||||
ASSERT_TRUE(blob);
|
||||
auto atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob));
|
||||
auto old_packer = atlas_context->GetRectPacker();
|
||||
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
@ -265,8 +270,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
|
||||
|
||||
auto blob2 = SkTextBlob::MakeFromString("spooky 1", sk_font);
|
||||
auto next_atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kColorBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob2));
|
||||
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kColorBitmap, 1.0f,
|
||||
atlas_context, TextFrameFromTextBlob(blob2));
|
||||
ASSERT_NE(atlas, next_atlas);
|
||||
auto* second_texture = next_atlas->GetTexture().get();
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user