From 05d77a51fe785820e9b011dc1cec83855beef749 Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 4 May 2023 16:28:58 -0700 Subject: [PATCH 1/5] [Impeller] remove SDF code paths to test only --- impeller/entity/contents/content_context.cc | 2 - impeller/entity/contents/content_context.h | 10 -- impeller/entity/contents/text_contents.cc | 46 ++----- impeller/entity/contents/text_contents.h | 5 - impeller/entity/entity_unittests.cc | 28 ---- impeller/typographer/BUILD.gn | 2 + .../backends/skia/text_render_context_skia.cc | 123 ----------------- .../backends/skia/text_sdf_atlas.cc | 126 ++++++++++++++++++ .../backends/skia/text_sdf_atlas.h | 17 +++ impeller/typographer/glyph_atlas.h | 7 - impeller/typographer/typographer_unittests.cc | 27 ++++ 11 files changed, 181 insertions(+), 212 deletions(-) create mode 100644 impeller/typographer/backends/skia/text_sdf_atlas.cc create mode 100644 impeller/typographer/backends/skia/text_sdf_atlas.h diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index a8f1e8e54151b..48257e494006e 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -286,8 +286,6 @@ ContentContext::ContentContext(std::shared_ptr context) CreateDefaultPipeline(*context_); glyph_atlas_pipelines_[{}] = CreateDefaultPipeline(*context_); - glyph_atlas_sdf_pipelines_[{}] = - CreateDefaultPipeline(*context_); geometry_color_pipelines_[{}] = CreateDefaultPipeline(*context_); yuv_to_rgb_filter_pipelines_[{}] = diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 65941ceda19f4..971b0cc4b082b 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -32,8 +32,6 @@ #include "impeller/entity/conical_gradient_fill.frag.h" #include "impeller/entity/glyph_atlas.frag.h" #include "impeller/entity/glyph_atlas.vert.h" -#include "impeller/entity/glyph_atlas_sdf.frag.h" -#include "impeller/entity/glyph_atlas_sdf.vert.h" #include "impeller/entity/gradient_fill.vert.h" #include "impeller/entity/linear_gradient_fill.frag.h" #include "impeller/entity/linear_to_srgb_filter.frag.h" @@ -173,8 +171,6 @@ using SrgbToLinearFilterPipeline = SrgbToLinearFilterFragmentShader>; using GlyphAtlasPipeline = RenderPipelineT; -using GlyphAtlasSdfPipeline = - RenderPipelineT; using PorterDuffBlendPipeline = RenderPipelineT; // Instead of requiring new shaders for clips, the solid fill stages are used @@ -473,11 +469,6 @@ class ContentContext { return GetPipeline(glyph_atlas_pipelines_, opts); } - std::shared_ptr> GetGlyphAtlasSdfPipeline( - ContentContextOptions opts) const { - return GetPipeline(glyph_atlas_sdf_pipelines_, opts); - } - std::shared_ptr> GetGeometryColorPipeline( ContentContextOptions opts) const { return GetPipeline(geometry_color_pipelines_, opts); @@ -731,7 +722,6 @@ class ContentContext { mutable Variants srgb_to_linear_filter_pipelines_; mutable Variants clip_pipelines_; mutable Variants glyph_atlas_pipelines_; - mutable Variants glyph_atlas_sdf_pipelines_; mutable Variants geometry_color_pipelines_; mutable Variants yuv_to_rgb_filter_pipelines_; mutable Variants porter_duff_blend_pipelines_; diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 6a4f0be4753c1..9e0662c242fee 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -74,7 +74,6 @@ std::optional TextContents::GetCoverage(const Entity& entity) const { return bounds->TransformBounds(entity.GetTransformation()); } -template static bool CommonRender( const ContentContext& renderer, const Entity& entity, @@ -85,11 +84,11 @@ static bool CommonRender( std::shared_ptr atlas, // NOLINT(performance-unnecessary-value-param) Command& cmd) { - using VS = typename TPipeline::VertexShader; - using FS = typename TPipeline::FragmentShader; + using VS = GlyphAtlasPipeline::VertexShader; + using FS = GlyphAtlasPipeline::FragmentShader; // Common vertex uniforms for all glyphs. - typename VS::FrameInfo frame_info; + VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); VS::BindFrameInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); @@ -109,7 +108,7 @@ static bool CommonRender( } sampler_desc.mip_filter = MipFilter::kNearest; - typename FS::FragInfo frag_info; + FS::FragInfo frag_info; frag_info.text_color = ToVector(color.Premultiply()); FS::BindFragInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frag_info)); @@ -183,7 +182,7 @@ static bool CommonRender( .Round(); for (const auto& point : unit_points) { - typename VS::PerVertexData vtx; + VS::PerVertexData vtx; if (entity.GetTransformation().IsTranslationScaleOnly()) { // Rouding up here prevents the bounds from becoming 1 pixel too small @@ -199,11 +198,8 @@ static bool CommonRender( point * glyph_position.glyph.bounds.size); } vtx.uv = uv_origin + point * uv_size; - - if constexpr (std::is_same_v) { - vtx.has_color = - glyph_position.glyph.type == Glyph::Type::kBitmap ? 1.0 : 0.0; - } + vtx.has_color = + glyph_position.glyph.type == Glyph::Type::kBitmap ? 1.0 : 0.0; vertex_builder.AppendVertex(std::move(vtx)); } @@ -220,30 +216,6 @@ static bool CommonRender( return true; } -bool TextContents::RenderSdf(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const { - auto atlas = - ResolveAtlas(GlyphAtlas::Type::kSignedDistanceField, - renderer.GetGlyphAtlasContext(), 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 = "TextFrameSDF"; - auto opts = OptionsFromPassAndEntity(pass, entity); - opts.primitive_type = PrimitiveType::kTriangle; - cmd.pipeline = renderer.GetGlyphAtlasSdfPipeline(opts); - cmd.stencil_reference = entity.GetStencilDepth(); - - return CommonRender(renderer, entity, pass, GetColor(), - frame_, offset_, atlas, cmd); -} - bool TextContents::Render(const ContentContext& renderer, const Entity& entity, RenderPass& pass) const { @@ -276,8 +248,8 @@ bool TextContents::Render(const ContentContext& renderer, cmd.pipeline = renderer.GetGlyphAtlasPipeline(opts); cmd.stencil_reference = entity.GetStencilDepth(); - return CommonRender(renderer, entity, pass, color, frame_, - offset_, atlas, cmd); + return CommonRender(renderer, entity, pass, color, frame_, offset_, atlas, + cmd); } } // namespace impeller diff --git a/impeller/entity/contents/text_contents.h b/impeller/entity/contents/text_contents.h index b32f37258d91c..e313abad9b715 100644 --- a/impeller/entity/contents/text_contents.h +++ b/impeller/entity/contents/text_contents.h @@ -48,11 +48,6 @@ class TextContents final : public Contents { const Entity& entity, RenderPass& pass) const override; - // TODO(dnfield): remove this https://github.com/flutter/flutter/issues/111640 - bool RenderSdf(const ContentContext& renderer, - const Entity& entity, - RenderPass& pass) const; - private: TextFrame frame_; Color color_; diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 007c6757a1696..77acc57dd912b 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2151,34 +2151,6 @@ TEST_P(EntityTest, TTTBlendColor) { } } -TEST_P(EntityTest, SdfText) { - auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { - SkFont font; - font.setSize(30); - auto blob = SkTextBlob::MakeFromString( - "the quick brown fox jumped over the lazy dog (but with sdf).", font); - auto frame = TextFrameFromTextBlob(blob); - auto lazy_glyph_atlas = std::make_shared(); - lazy_glyph_atlas->AddTextFrame(frame); - - EXPECT_FALSE(lazy_glyph_atlas->HasColor()); - - auto text_contents = std::make_shared(); - text_contents->SetTextFrame(frame); - text_contents->SetGlyphAtlas(std::move(lazy_glyph_atlas)); - text_contents->SetColor(Color(1.0, 0.0, 0.0, 1.0)); - Entity entity; - entity.SetTransformation( - Matrix::MakeTranslation(Vector3{200.0, 200.0, 0.0}) * - Matrix::MakeScale(GetContentScale())); - entity.SetContents(text_contents); - - // Force SDF rendering. - return text_contents->RenderSdf(context, entity, pass); - }; - ASSERT_TRUE(OpenPlaygroundHere(callback)); -} - TEST_P(EntityTest, AtlasContentsSubAtlas) { auto boston = CreateTextureForFixture("boston.jpg"); diff --git a/impeller/typographer/BUILD.gn b/impeller/typographer/BUILD.gn index 761b9af1e618c..95acdc59ab6f5 100644 --- a/impeller/typographer/BUILD.gn +++ b/impeller/typographer/BUILD.gn @@ -10,6 +10,8 @@ impeller_component("typographer") { "backends/skia/text_frame_skia.h", "backends/skia/text_render_context_skia.cc", "backends/skia/text_render_context_skia.h", + "backends/skia/text_sdf_atlas.cc", + "backends/skia/text_sdf_atlas.h", "backends/skia/typeface_skia.cc", "backends/skia/typeface_skia.h", "font.cc", diff --git a/impeller/typographer/backends/skia/text_render_context_skia.cc b/impeller/typographer/backends/skia/text_render_context_skia.cc index 51ec87b712d30..886674ea456f8 100644 --- a/impeller/typographer/backends/skia/text_render_context_skia.cc +++ b/impeller/typographer/backends/skia/text_render_context_skia.cc @@ -49,9 +49,6 @@ static FontGlyphPair::Set CollectUniqueFontGlyphPairs( while (const TextFrame* frame = frame_iterator()) { for (const TextRun& run : frame->GetRuns()) { const Font& font = run.GetFont(); - // TODO(dnfield): If we're doing SDF here, we should be using a consistent - // point size. - // https://github.com/flutter/flutter/issues/112016 for (const TextRun::GlyphPosition& glyph_position : run.GetGlyphPositions()) { set.insert({font, glyph_position.glyph}); @@ -171,121 +168,6 @@ ISize OptimumAtlasSizeForFontGlyphPairs( } } // namespace -/// Compute signed-distance field for an 8-bpp grayscale image (values greater -/// than 127 are considered "on") For details of this algorithm, see "The 'dead -/// reckoning' signed distance transform" [Grevera 2004] -static void ConvertBitmapToSignedDistanceField(uint8_t* pixels, - uint16_t width, - uint16_t height) { - if (!pixels || width == 0 || height == 0) { - return; - } - - using ShortPoint = TPoint; - - // distance to nearest boundary point map - std::vector distance_map(width * height); - // nearest boundary point map - std::vector boundary_point_map(width * height); - - // Some helpers for manipulating the above arrays -#define image(_x, _y) (pixels[(_y)*width + (_x)] > 0x7f) -#define distance(_x, _y) distance_map[(_y)*width + (_x)] -#define nearestpt(_x, _y) boundary_point_map[(_y)*width + (_x)] - - const Scalar maxDist = hypot(width, height); - const Scalar distUnit = 1; - const Scalar distDiag = sqrt(2); - - // Initialization phase: set all distances to "infinity"; zero out nearest - // boundary point map - for (uint16_t y = 0; y < height; ++y) { - for (uint16_t x = 0; x < width; ++x) { - distance(x, y) = maxDist; - nearestpt(x, y) = ShortPoint{0, 0}; - } - } - - // Immediate interior/exterior phase: mark all points along the boundary as - // such - for (uint16_t y = 1; y < height - 1; ++y) { - for (uint16_t x = 1; x < width - 1; ++x) { - bool inside = image(x, y); - if (image(x - 1, y) != inside || image(x + 1, y) != inside || - image(x, y - 1) != inside || image(x, y + 1) != inside) { - distance(x, y) = 0; - nearestpt(x, y) = ShortPoint{x, y}; - } - } - } - - // Forward dead-reckoning pass - for (uint16_t y = 1; y < height - 2; ++y) { - for (uint16_t x = 1; x < width - 2; ++x) { - if (distance_map[(y - 1) * width + (x - 1)] + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x - 1, y - 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x, y - 1) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x, y - 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x + 1, y - 1) + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x + 1, y - 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x - 1, y) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x - 1, y); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - } - } - - // Backward dead-reckoning pass - for (uint16_t y = height - 2; y >= 1; --y) { - for (uint16_t x = width - 2; x >= 1; --x) { - if (distance(x + 1, y) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x + 1, y); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x - 1, y + 1) + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x - 1, y + 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x, y + 1) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x, y + 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x + 1, y + 1) + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x + 1, y + 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - } - } - - // Interior distance negation pass; distances outside the figure are - // considered negative - // Also does final quantization. - for (uint16_t y = 0; y < height; ++y) { - for (uint16_t x = 0; x < width; ++x) { - if (!image(x, y)) { - distance(x, y) = -distance(x, y); - } - - float norm_factor = 13.5; - float dist = distance(x, y); - float clamped_dist = fmax(-norm_factor, fmin(dist, norm_factor)); - float scaled_dist = clamped_dist / norm_factor; - uint8_t quantized_value = ((scaled_dist + 1) / 2) * UINT8_MAX; - pixels[y * width + x] = quantized_value; - } - } - -#undef image -#undef distance -#undef nearestpt -} - static void DrawGlyph(SkCanvas* canvas, const FontGlyphPair& font_glyph, const Rect& location, @@ -353,7 +235,6 @@ static std::shared_ptr CreateAtlasBitmap(const GlyphAtlas& atlas, SkImageInfo image_info; switch (atlas.GetType()) { - case GlyphAtlas::Type::kSignedDistanceField: case GlyphAtlas::Type::kAlphaBitmap: image_info = SkImageInfo::MakeA8(atlas_size.width, atlas_size.height); break; @@ -563,10 +444,6 @@ std::shared_ptr TextRenderContextSkia::CreateGlyphAtlas( // --------------------------------------------------------------------------- PixelFormat format; switch (type) { - case GlyphAtlas::Type::kSignedDistanceField: - ConvertBitmapToSignedDistanceField( - reinterpret_cast(bitmap->getPixels()), atlas_size.width, - atlas_size.height); case GlyphAtlas::Type::kAlphaBitmap: format = PixelFormat::kA8UNormInt; break; diff --git a/impeller/typographer/backends/skia/text_sdf_atlas.cc b/impeller/typographer/backends/skia/text_sdf_atlas.cc new file mode 100644 index 0000000000000..fab23d17acf1b --- /dev/null +++ b/impeller/typographer/backends/skia/text_sdf_atlas.cc @@ -0,0 +1,126 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/typographer/backends/skia/text_sdf_atlas.h" + +#include + +#include "impeller/geometry/point.h" +#include "impeller/geometry/scalar.h" + +namespace impeller { + +void ConvertBitmapToSignedDistanceField(uint8_t* pixels, + uint16_t width, + uint16_t height) { + if (!pixels || width == 0 || height == 0) { + return; + } + + using ShortPoint = TPoint; + + // distance to nearest boundary point map + std::vector distance_map(width * height); + // nearest boundary point map + std::vector boundary_point_map(width * height); + + // Some helpers for manipulating the above arrays +#define image(_x, _y) (pixels[(_y)*width + (_x)] > 0x7f) +#define distance(_x, _y) distance_map[(_y)*width + (_x)] +#define nearestpt(_x, _y) boundary_point_map[(_y)*width + (_x)] + + const Scalar maxDist = hypot(width, height); + const Scalar distUnit = 1; + const Scalar distDiag = sqrt(2); + + // Initialization phase: set all distances to "infinity"; zero out nearest + // boundary point map + for (uint16_t y = 0; y < height; ++y) { + for (uint16_t x = 0; x < width; ++x) { + distance(x, y) = maxDist; + nearestpt(x, y) = ShortPoint{0, 0}; + } + } + + // Immediate interior/exterior phase: mark all points along the boundary as + // such + for (uint16_t y = 1; y < height - 1; ++y) { + for (uint16_t x = 1; x < width - 1; ++x) { + bool inside = image(x, y); + if (image(x - 1, y) != inside || image(x + 1, y) != inside || + image(x, y - 1) != inside || image(x, y + 1) != inside) { + distance(x, y) = 0; + nearestpt(x, y) = ShortPoint{x, y}; + } + } + } + + // Forward dead-reckoning pass + for (uint16_t y = 1; y < height - 2; ++y) { + for (uint16_t x = 1; x < width - 2; ++x) { + if (distance_map[(y - 1) * width + (x - 1)] + distDiag < distance(x, y)) { + nearestpt(x, y) = nearestpt(x - 1, y - 1); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + if (distance(x, y - 1) + distUnit < distance(x, y)) { + nearestpt(x, y) = nearestpt(x, y - 1); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + if (distance(x + 1, y - 1) + distDiag < distance(x, y)) { + nearestpt(x, y) = nearestpt(x + 1, y - 1); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + if (distance(x - 1, y) + distUnit < distance(x, y)) { + nearestpt(x, y) = nearestpt(x - 1, y); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + } + } + + // Backward dead-reckoning pass + for (uint16_t y = height - 2; y >= 1; --y) { + for (uint16_t x = width - 2; x >= 1; --x) { + if (distance(x + 1, y) + distUnit < distance(x, y)) { + nearestpt(x, y) = nearestpt(x + 1, y); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + if (distance(x - 1, y + 1) + distDiag < distance(x, y)) { + nearestpt(x, y) = nearestpt(x - 1, y + 1); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + if (distance(x, y + 1) + distUnit < distance(x, y)) { + nearestpt(x, y) = nearestpt(x, y + 1); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + if (distance(x + 1, y + 1) + distDiag < distance(x, y)) { + nearestpt(x, y) = nearestpt(x + 1, y + 1); + distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); + } + } + } + + // Interior distance negation pass; distances outside the figure are + // considered negative + // Also does final quantization. + for (uint16_t y = 0; y < height; ++y) { + for (uint16_t x = 0; x < width; ++x) { + if (!image(x, y)) { + distance(x, y) = -distance(x, y); + } + + float norm_factor = 13.5; + float dist = distance(x, y); + float clamped_dist = fmax(-norm_factor, fmin(dist, norm_factor)); + float scaled_dist = clamped_dist / norm_factor; + uint8_t quantized_value = ((scaled_dist + 1) / 2) * UINT8_MAX; + pixels[y * width + x] = quantized_value; + } + } + +#undef image +#undef distance +#undef nearestpt +} + +} // namespace impeller diff --git a/impeller/typographer/backends/skia/text_sdf_atlas.h b/impeller/typographer/backends/skia/text_sdf_atlas.h new file mode 100644 index 0000000000000..1831b2db625f7 --- /dev/null +++ b/impeller/typographer/backends/skia/text_sdf_atlas.h @@ -0,0 +1,17 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +namespace impeller { + +/// Compute signed-distance field for an 8-bpp grayscale image (values greater +/// than 127 are considered "on") For details of this algorithm, see "The 'dead +/// reckoning' signed distance transform" [Grevera 2004] +void ConvertBitmapToSignedDistanceField(uint8_t* pixels, + uint16_t width, + uint16_t height); +} // namespace impeller diff --git a/impeller/typographer/glyph_atlas.h b/impeller/typographer/glyph_atlas.h index e7282a0cdb6a0..e8f2cbe451309 100644 --- a/impeller/typographer/glyph_atlas.h +++ b/impeller/typographer/glyph_atlas.h @@ -32,13 +32,6 @@ class GlyphAtlas { //---------------------------------------------------------------------------- /// @brief Describes how the glyphs are represented in the texture. enum class Type { - //-------------------------------------------------------------------------- - /// The glyphs are represented at a fixed size in an 8-bit grayscale texture - /// where the value of each pixel represents a signed-distance field that - /// stores the glyph outlines. - /// - kSignedDistanceField, - //-------------------------------------------------------------------------- /// The glyphs are reprsented at their requested size using only an 8-bit /// alpha channel. diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc index 445cc283fe6c7..f9b1ce7993981 100644 --- a/impeller/typographer/typographer_unittests.cc +++ b/impeller/typographer/typographer_unittests.cc @@ -6,7 +6,9 @@ #include "impeller/playground/playground_test.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "impeller/typographer/backends/skia/text_render_context_skia.h" +#include "impeller/typographer/backends/skia/text_sdf_atlas.h" #include "impeller/typographer/lazy_glyph_atlas.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkTextBlob.h" @@ -282,5 +284,30 @@ TEST_P(TypographerTest, MaybeHasOverlapping) { ASSERT_TRUE(frame_2.MaybeHasOverlapping()); } +TEST_P(TypographerTest, ValidateSDFFunctions) { + auto context = TextRenderContext::Create(GetContext()); + auto atlas_context = std::make_shared(); + + SkFont sk_font; + auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font); + ASSERT_TRUE(blob); + + auto atlas = + context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context, + TextFrameFromTextBlob(blob)); + + auto pixels = + reinterpret_cast(atlas_context->GetBitmap()->getPixels()); + auto atlas_size = atlas_context->GetAtlasSize(); + ConvertBitmapToSignedDistanceField(pixels, atlas_size.width, + atlas_size.height); + // Validate that there is at least one non-zero pixel. + bool non_zero = false; + for (auto i = 0u; i < atlas_size.width * atlas_size.height; i++) { + non_zero |= pixels[i] != 0u; + } + ASSERT_TRUE(non_zero); +} + } // namespace testing } // namespace impeller From 5a7ecc46beed2154cc00498a84ec5c43a7d0673f Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 4 May 2023 16:42:59 -0700 Subject: [PATCH 2/5] license --- ci/licenses_golden/licenses_flutter | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ffcb36dbe7f3f..b0cd03d0a52ee 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1648,6 +1648,8 @@ ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.cc + ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/font.cc + ../../../flutter/LICENSE @@ -4243,6 +4245,8 @@ FILE: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.cc FILE: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.h FILE: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.cc FILE: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.h +FILE: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.cc +FILE: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.h FILE: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.cc FILE: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.h FILE: ../../../flutter/impeller/typographer/font.cc From f24aa9adc7eafa1016429ca5655dd5c9ed4f809b Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Thu, 4 May 2023 21:12:44 -0700 Subject: [PATCH 3/5] ++ --- ci/licenses_golden/licenses_flutter | 4 - impeller/typographer/BUILD.gn | 2 - .../backends/skia/text_sdf_atlas.cc | 126 ------------------ .../backends/skia/text_sdf_atlas.h | 17 --- impeller/typographer/typographer_unittests.cc | 26 ---- 5 files changed, 175 deletions(-) delete mode 100644 impeller/typographer/backends/skia/text_sdf_atlas.cc delete mode 100644 impeller/typographer/backends/skia/text_sdf_atlas.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index b0cd03d0a52ee..ffcb36dbe7f3f 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1648,8 +1648,6 @@ ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.cc + ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.h + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.cc + ../../../flutter/LICENSE -ORIGIN: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/typographer/font.cc + ../../../flutter/LICENSE @@ -4245,8 +4243,6 @@ FILE: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.cc FILE: ../../../flutter/impeller/typographer/backends/skia/text_frame_skia.h FILE: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.cc FILE: ../../../flutter/impeller/typographer/backends/skia/text_render_context_skia.h -FILE: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.cc -FILE: ../../../flutter/impeller/typographer/backends/skia/text_sdf_atlas.h FILE: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.cc FILE: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.h FILE: ../../../flutter/impeller/typographer/font.cc diff --git a/impeller/typographer/BUILD.gn b/impeller/typographer/BUILD.gn index 95acdc59ab6f5..761b9af1e618c 100644 --- a/impeller/typographer/BUILD.gn +++ b/impeller/typographer/BUILD.gn @@ -10,8 +10,6 @@ impeller_component("typographer") { "backends/skia/text_frame_skia.h", "backends/skia/text_render_context_skia.cc", "backends/skia/text_render_context_skia.h", - "backends/skia/text_sdf_atlas.cc", - "backends/skia/text_sdf_atlas.h", "backends/skia/typeface_skia.cc", "backends/skia/typeface_skia.h", "font.cc", diff --git a/impeller/typographer/backends/skia/text_sdf_atlas.cc b/impeller/typographer/backends/skia/text_sdf_atlas.cc deleted file mode 100644 index fab23d17acf1b..0000000000000 --- a/impeller/typographer/backends/skia/text_sdf_atlas.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "impeller/typographer/backends/skia/text_sdf_atlas.h" - -#include - -#include "impeller/geometry/point.h" -#include "impeller/geometry/scalar.h" - -namespace impeller { - -void ConvertBitmapToSignedDistanceField(uint8_t* pixels, - uint16_t width, - uint16_t height) { - if (!pixels || width == 0 || height == 0) { - return; - } - - using ShortPoint = TPoint; - - // distance to nearest boundary point map - std::vector distance_map(width * height); - // nearest boundary point map - std::vector boundary_point_map(width * height); - - // Some helpers for manipulating the above arrays -#define image(_x, _y) (pixels[(_y)*width + (_x)] > 0x7f) -#define distance(_x, _y) distance_map[(_y)*width + (_x)] -#define nearestpt(_x, _y) boundary_point_map[(_y)*width + (_x)] - - const Scalar maxDist = hypot(width, height); - const Scalar distUnit = 1; - const Scalar distDiag = sqrt(2); - - // Initialization phase: set all distances to "infinity"; zero out nearest - // boundary point map - for (uint16_t y = 0; y < height; ++y) { - for (uint16_t x = 0; x < width; ++x) { - distance(x, y) = maxDist; - nearestpt(x, y) = ShortPoint{0, 0}; - } - } - - // Immediate interior/exterior phase: mark all points along the boundary as - // such - for (uint16_t y = 1; y < height - 1; ++y) { - for (uint16_t x = 1; x < width - 1; ++x) { - bool inside = image(x, y); - if (image(x - 1, y) != inside || image(x + 1, y) != inside || - image(x, y - 1) != inside || image(x, y + 1) != inside) { - distance(x, y) = 0; - nearestpt(x, y) = ShortPoint{x, y}; - } - } - } - - // Forward dead-reckoning pass - for (uint16_t y = 1; y < height - 2; ++y) { - for (uint16_t x = 1; x < width - 2; ++x) { - if (distance_map[(y - 1) * width + (x - 1)] + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x - 1, y - 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x, y - 1) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x, y - 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x + 1, y - 1) + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x + 1, y - 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x - 1, y) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x - 1, y); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - } - } - - // Backward dead-reckoning pass - for (uint16_t y = height - 2; y >= 1; --y) { - for (uint16_t x = width - 2; x >= 1; --x) { - if (distance(x + 1, y) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x + 1, y); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x - 1, y + 1) + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x - 1, y + 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x, y + 1) + distUnit < distance(x, y)) { - nearestpt(x, y) = nearestpt(x, y + 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - if (distance(x + 1, y + 1) + distDiag < distance(x, y)) { - nearestpt(x, y) = nearestpt(x + 1, y + 1); - distance(x, y) = hypot(x - nearestpt(x, y).x, y - nearestpt(x, y).y); - } - } - } - - // Interior distance negation pass; distances outside the figure are - // considered negative - // Also does final quantization. - for (uint16_t y = 0; y < height; ++y) { - for (uint16_t x = 0; x < width; ++x) { - if (!image(x, y)) { - distance(x, y) = -distance(x, y); - } - - float norm_factor = 13.5; - float dist = distance(x, y); - float clamped_dist = fmax(-norm_factor, fmin(dist, norm_factor)); - float scaled_dist = clamped_dist / norm_factor; - uint8_t quantized_value = ((scaled_dist + 1) / 2) * UINT8_MAX; - pixels[y * width + x] = quantized_value; - } - } - -#undef image -#undef distance -#undef nearestpt -} - -} // namespace impeller diff --git a/impeller/typographer/backends/skia/text_sdf_atlas.h b/impeller/typographer/backends/skia/text_sdf_atlas.h deleted file mode 100644 index 1831b2db625f7..0000000000000 --- a/impeller/typographer/backends/skia/text_sdf_atlas.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include - -namespace impeller { - -/// Compute signed-distance field for an 8-bpp grayscale image (values greater -/// than 127 are considered "on") For details of this algorithm, see "The 'dead -/// reckoning' signed distance transform" [Grevera 2004] -void ConvertBitmapToSignedDistanceField(uint8_t* pixels, - uint16_t width, - uint16_t height); -} // namespace impeller diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc index f9b1ce7993981..b35e3d7eca6a3 100644 --- a/impeller/typographer/typographer_unittests.cc +++ b/impeller/typographer/typographer_unittests.cc @@ -6,7 +6,6 @@ #include "impeller/playground/playground_test.h" #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "impeller/typographer/backends/skia/text_render_context_skia.h" -#include "impeller/typographer/backends/skia/text_sdf_atlas.h" #include "impeller/typographer/lazy_glyph_atlas.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkData.h" @@ -284,30 +283,5 @@ TEST_P(TypographerTest, MaybeHasOverlapping) { ASSERT_TRUE(frame_2.MaybeHasOverlapping()); } -TEST_P(TypographerTest, ValidateSDFFunctions) { - auto context = TextRenderContext::Create(GetContext()); - auto atlas_context = std::make_shared(); - - SkFont sk_font; - auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font); - ASSERT_TRUE(blob); - - auto atlas = - context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context, - TextFrameFromTextBlob(blob)); - - auto pixels = - reinterpret_cast(atlas_context->GetBitmap()->getPixels()); - auto atlas_size = atlas_context->GetAtlasSize(); - ConvertBitmapToSignedDistanceField(pixels, atlas_size.width, - atlas_size.height); - // Validate that there is at least one non-zero pixel. - bool non_zero = false; - for (auto i = 0u; i < atlas_size.width * atlas_size.height; i++) { - non_zero |= pixels[i] != 0u; - } - ASSERT_TRUE(non_zero); -} - } // namespace testing } // namespace impeller From 2585a55b302b98c94386626e44f45cca4df5b8eb Mon Sep 17 00:00:00 2001 From: jonahwilliams Date: Fri, 5 May 2023 09:27:31 -0700 Subject: [PATCH 4/5] fix std move errors --- impeller/entity/contents/text_contents.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 9e0662c242fee..1fba560b176f2 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -201,13 +201,13 @@ static bool CommonRender( vtx.has_color = glyph_position.glyph.type == Glyph::Type::kBitmap ? 1.0 : 0.0; - vertex_builder.AppendVertex(std::move(vtx)); + vertex_builder.AppendVertex(vtx); } } } auto vertex_buffer = vertex_builder.CreateVertexBuffer(pass.GetTransientsBuffer()); - cmd.BindVertices(std::move(vertex_buffer)); + cmd.BindVertices(vertex_buffer); if (!pass.AddCommand(cmd)) { return false; From 17a3a87b523fa6159be5a49f8f954be2a1b2f850 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Fri, 5 May 2023 09:37:03 -0700 Subject: [PATCH 5/5] Update typographer_unittests.cc --- impeller/typographer/typographer_unittests.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/impeller/typographer/typographer_unittests.cc b/impeller/typographer/typographer_unittests.cc index b35e3d7eca6a3..445cc283fe6c7 100644 --- a/impeller/typographer/typographer_unittests.cc +++ b/impeller/typographer/typographer_unittests.cc @@ -7,7 +7,6 @@ #include "impeller/typographer/backends/skia/text_frame_skia.h" #include "impeller/typographer/backends/skia/text_render_context_skia.h" #include "impeller/typographer/lazy_glyph_atlas.h" -#include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkTextBlob.h"