[Impeller] remove STB backend. (flutter/engine#55842)

Delete the STB typographer backend.

Fixes https://github.com/flutter/flutter/issues/156429
This commit is contained in:
Jonah Williams 2024-10-14 11:16:20 -07:00 committed by GitHub
parent dd68ebf2fb
commit 61460afe48
16 changed files with 0 additions and 1044 deletions

4
DEPS
View File

@ -226,7 +226,6 @@ vars = {
"upstream_sqlite": "https://github.com/sqlite/sqlite.git",
"upstream_sse": "https://github.com/dart-lang/sse.git",
"upstream_stack_trace": "https://github.com/dart-lang/stack_trace.git",
"upstream_stb": "https://github.com/nothings/stb.git",
"upstream_stream_channel": "https://github.com/dart-lang/stream_channel.git",
"upstream_string_scanner": "https://github.com/dart-lang/string_scanner.git",
"upstream_SwiftShader": "https://swiftshader.googlesource.com/SwiftShader.git",
@ -717,9 +716,6 @@ deps = {
'src/flutter/third_party/json':
Var('flutter_git') + '/third_party/json.git' + '@' + '17d9eacd248f58b73f4d1be518ef649fe2295642',
'src/flutter/third_party/stb':
Var('flutter_git') + '/third_party/stb.git' + '@' + '5736b15f7ea0ffb08dd38af21067c314d6a3aae9',
'src/flutter/third_party/gradle': {
'packages': [
{

View File

@ -2946,7 +2946,6 @@
../../../flutter/third_party/sqlite/Makefile
../../../flutter/third_party/sqlite/README.md
../../../flutter/third_party/sqlite/VERSION
../../../flutter/third_party/stb
../../../flutter/third_party/swiftshader/.clang-format
../../../flutter/third_party/swiftshader/.git
../../../flutter/third_party/swiftshader/.gitignore

View File

@ -43318,14 +43318,6 @@ ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.cc + .
ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typographer_context_skia.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/skia/typographer_context_skia.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/glyph_atlas_context_stb.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/glyph_atlas_context_stb.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/text_frame_stb.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/text_frame_stb.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/typeface_stb.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/typeface_stb.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/typographer_context_stb.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/backends/stb/typographer_context_stb.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/font.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/font.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/typographer/font_glyph_pair.cc + ../../../flutter/LICENSE
@ -46201,14 +46193,6 @@ FILE: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.cc
FILE: ../../../flutter/impeller/typographer/backends/skia/typeface_skia.h
FILE: ../../../flutter/impeller/typographer/backends/skia/typographer_context_skia.cc
FILE: ../../../flutter/impeller/typographer/backends/skia/typographer_context_skia.h
FILE: ../../../flutter/impeller/typographer/backends/stb/glyph_atlas_context_stb.cc
FILE: ../../../flutter/impeller/typographer/backends/stb/glyph_atlas_context_stb.h
FILE: ../../../flutter/impeller/typographer/backends/stb/text_frame_stb.cc
FILE: ../../../flutter/impeller/typographer/backends/stb/text_frame_stb.h
FILE: ../../../flutter/impeller/typographer/backends/stb/typeface_stb.cc
FILE: ../../../flutter/impeller/typographer/backends/stb/typeface_stb.h
FILE: ../../../flutter/impeller/typographer/backends/stb/typographer_context_stb.cc
FILE: ../../../flutter/impeller/typographer/backends/stb/typographer_context_stb.h
FILE: ../../../flutter/impeller/typographer/font.cc
FILE: ../../../flutter/impeller/typographer/font.h
FILE: ../../../flutter/impeller/typographer/font_glyph_pair.cc

View File

@ -102,7 +102,6 @@ template("display_list_unittests_component") {
":display_list",
"../playground:playground_test",
"//flutter/impeller/golden_tests:screenshot",
"//flutter/impeller/typographer/backends/stb:typographer_stb_backend",
"//flutter/third_party/txt",
]
if (defined(invoker.public_configs)) {
@ -178,7 +177,6 @@ template("aiks_unittests_component") {
"//flutter/impeller/geometry:geometry_asserts",
"//flutter/impeller/golden_tests:golden_playground_test",
"//flutter/impeller/playground:playground_test",
"//flutter/impeller/typographer/backends/stb:typographer_stb_backend",
"//flutter/testing:testing_lib",
"//flutter/third_party/txt",
]

View File

@ -15,9 +15,6 @@
#include "flutter/testing/testing.h"
#include "impeller/geometry/matrix.h"
#include "impeller/typographer/backends/skia/text_frame_skia.h"
#include "impeller/typographer/backends/stb/text_frame_stb.h"
#include "impeller/typographer/backends/stb/typeface_stb.h"
#include "impeller/typographer/backends/stb/typographer_context_stb.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkRect.h"
@ -81,40 +78,6 @@ bool RenderTextInCanvasSkia(const std::shared_ptr<Context>& context,
return true;
}
bool RenderTextInCanvasSTB(const std::shared_ptr<Context>& context,
DisplayListBuilder& canvas,
const std::string& text,
const std::string& font_fixture,
const TextRenderOptions& options = {}) {
// Draw the baseline.
DlPaint paint;
paint.setColor(DlColor::kAqua().withAlpha(255 * 0.25));
canvas.DrawRect(SkRect::MakeXYWH(options.position.x() - 50,
options.position.y(), 900, 10),
paint);
// Mark the point at which the text is drawn.
paint.setColor(DlColor::kRed().withAlpha(255 * 0.25));
canvas.DrawCircle(options.position, 5.0, paint);
// Construct the text blob.
auto mapping = flutter::testing::OpenFixtureAsMapping(font_fixture.c_str());
if (!mapping) {
return false;
}
auto typeface_stb = std::make_shared<TypefaceSTB>(std::move(mapping));
auto frame = MakeTextFrameSTB(
typeface_stb, Font::Metrics{.point_size = options.font_size}, text);
DlPaint text_paint;
text_paint.setColor(options.color);
canvas.DrawTextFrame(frame, options.position.x(), options.position.y(),
text_paint);
return true;
}
TEST_P(AiksTest, CanRenderTextFrame) {
DisplayListBuilder builder;
@ -204,21 +167,6 @@ TEST_P(AiksTest, CanRenderTextFrameWithFractionScaling) {
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, CanRenderTextFrameSTB) {
DisplayListBuilder builder;
DlPaint paint;
paint.setColor(DlColor::ARGB(1, 0.1, 0.1, 0.1));
builder.DrawPaint(paint);
ASSERT_TRUE(RenderTextInCanvasSTB(
GetContext(), builder, "the quick brown fox jumped over the lazy dog!.?",
"Roboto-Regular.ttf"));
SetTypographerContext(TypographerContextSTB::Make());
ASSERT_TRUE(OpenPlaygroundHere(builder.Build()));
}
TEST_P(AiksTest, TextFrameSubpixelAlignment) {
// "Random" numbers between 0 and 1. Hardcoded to avoid flakiness in goldens.
std::array<Scalar, 20> phase_offsets = {

View File

@ -45,7 +45,6 @@ impeller_component("typographer_unittests") {
deps = [
"../playground:playground_test",
"backends/skia:typographer_skia_backend",
"backends/stb:typographer_stb_backend",
"//flutter/display_list/testing:display_list_testing",
"//flutter/third_party/txt",
]

View File

@ -1,25 +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.
import("//flutter/impeller/tools/impeller.gni")
impeller_component("typographer_stb_backend") {
testonly = true
sources = [
"glyph_atlas_context_stb.cc",
"glyph_atlas_context_stb.h",
"text_frame_stb.cc",
"text_frame_stb.h",
"typeface_stb.cc",
"typeface_stb.h",
"typographer_context_stb.cc",
"typographer_context_stb.h",
]
public_deps = [
"//flutter/impeller/typographer",
"//flutter/third_party/stb:stb_truetype",
]
}

View File

@ -1,59 +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/stb/glyph_atlas_context_stb.h"
namespace impeller {
BitmapSTB::BitmapSTB() = default;
BitmapSTB::~BitmapSTB() = default;
BitmapSTB::BitmapSTB(size_t width, size_t height, size_t bytes_per_pixel)
: width_(width),
height_(height),
bytes_per_pixel_(bytes_per_pixel),
pixels_(std::vector<uint8_t>(width * height * bytes_per_pixel, 0)) {}
uint8_t* BitmapSTB::GetPixels() {
return pixels_.data();
}
uint8_t* BitmapSTB::GetPixelAddress(TPoint<size_t> coords) {
FML_DCHECK(coords.x < width_);
FML_DCHECK(coords.x < height_);
return &pixels_.data()[(coords.x + width_ * coords.y) * bytes_per_pixel_];
}
size_t BitmapSTB::GetRowBytes() const {
return width_ * bytes_per_pixel_;
}
size_t BitmapSTB::GetWidth() const {
return width_;
}
size_t BitmapSTB::GetHeight() const {
return height_;
}
size_t BitmapSTB::GetSize() const {
return width_ * height_ * bytes_per_pixel_;
}
GlyphAtlasContextSTB::GlyphAtlasContextSTB(GlyphAtlas::Type type)
: GlyphAtlasContext(type) {}
GlyphAtlasContextSTB::~GlyphAtlasContextSTB() = default;
std::shared_ptr<BitmapSTB> GlyphAtlasContextSTB::GetBitmap() const {
return bitmap_;
}
void GlyphAtlasContextSTB::UpdateBitmap(std::shared_ptr<BitmapSTB> bitmap) {
bitmap_ = std::move(bitmap);
}
} // namespace impeller

View File

@ -1,64 +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.
#ifndef FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_GLYPH_ATLAS_CONTEXT_STB_H_
#define FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_GLYPH_ATLAS_CONTEXT_STB_H_
#include "impeller/base/backend_cast.h"
#include "impeller/typographer/glyph_atlas.h"
namespace impeller {
class BitmapSTB {
public:
BitmapSTB();
~BitmapSTB();
BitmapSTB(size_t width, size_t height, size_t bytes_per_pixel);
uint8_t* GetPixels();
uint8_t* GetPixelAddress(TPoint<size_t> coords);
size_t GetRowBytes() const;
size_t GetWidth() const;
size_t GetHeight() const;
size_t GetSize() const;
private:
size_t width_ = 0;
size_t height_ = 0;
size_t bytes_per_pixel_ = 0;
std::vector<uint8_t> pixels_;
};
class GlyphAtlasContextSTB
: public GlyphAtlasContext,
public BackendCast<GlyphAtlasContextSTB, GlyphAtlasContext> {
public:
explicit GlyphAtlasContextSTB(GlyphAtlas::Type type);
~GlyphAtlasContextSTB() override;
//----------------------------------------------------------------------------
/// @brief Retrieve the previous (if any) BitmapSTB instance.
std::shared_ptr<BitmapSTB> GetBitmap() const;
void UpdateBitmap(std::shared_ptr<BitmapSTB> bitmap);
private:
std::shared_ptr<BitmapSTB> bitmap_;
GlyphAtlasContextSTB(const GlyphAtlasContextSTB&) = delete;
GlyphAtlasContextSTB& operator=(const GlyphAtlasContextSTB&) = delete;
};
} // namespace impeller
#endif // FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_GLYPH_ATLAS_CONTEXT_STB_H_

View File

@ -1,71 +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/stb/text_frame_stb.h"
#include "impeller/typographer/font.h"
namespace impeller {
std::shared_ptr<TextFrame> MakeTextFrameSTB(
const std::shared_ptr<TypefaceSTB>& typeface_stb,
Font::Metrics metrics,
const std::string& text) {
TextRun run(Font(typeface_stb, metrics, AxisAlignment::kNone));
// Shape the text run using STB. The glyph positions could also be resolved
// using a more advanced text shaper such as harfbuzz.
float scale = stbtt_ScaleForMappingEmToPixels(
typeface_stb->GetFontInfo(),
metrics.point_size * TypefaceSTB::kPointsToPixels);
int ascent, descent, line_gap;
stbtt_GetFontVMetrics(typeface_stb->GetFontInfo(), &ascent, &descent,
&line_gap);
ascent = std::round(ascent * scale);
descent = std::round(descent * scale);
float x = 0;
std::vector<Rect> bounds;
bounds.resize(text.size());
for (size_t i = 0; i < text.size(); i++) {
int glyph_index =
stbtt_FindGlyphIndex(typeface_stb->GetFontInfo(), text[i]);
int x0, y0, x1, y1;
stbtt_GetGlyphBitmapBox(typeface_stb->GetFontInfo(), glyph_index, scale,
scale, &x0, &y0, &x1, &y1);
float y = y0;
int advance_width;
int left_side_bearing;
stbtt_GetGlyphHMetrics(typeface_stb->GetFontInfo(), glyph_index,
&advance_width, &left_side_bearing);
bounds.push_back(Rect::MakeXYWH(0, 0, x1 - x0, y1 - y0));
Glyph glyph(glyph_index, Glyph::Type::kPath);
run.AddGlyph(glyph, {x + (left_side_bearing * scale), y});
if (i + 1 < text.size()) {
int kerning = stbtt_GetCodepointKernAdvance(typeface_stb->GetFontInfo(),
text[i], text[i + 1]);
x += std::round((advance_width + kerning) * scale);
}
}
std::optional<Rect> result;
for (auto i = 0u; i < bounds.size(); i++) {
const TextRun::GlyphPosition& gp = run.GetGlyphPositions()[i];
Rect glyph_rect = Rect::MakeOriginSize(gp.position + bounds[i].GetOrigin(),
bounds[i].GetSize());
result = result.has_value() ? result->Union(glyph_rect) : glyph_rect;
}
std::vector<TextRun> runs = {run};
return std::make_shared<TextFrame>(
runs, result.value_or(Rect::MakeLTRB(0, 0, 0, 0)), false);
}
} // namespace impeller

View File

@ -1,20 +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.
#ifndef FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TEXT_FRAME_STB_H_
#define FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TEXT_FRAME_STB_H_
#include "impeller/typographer/backends/stb/typeface_stb.h"
#include "impeller/typographer/text_frame.h"
namespace impeller {
std::shared_ptr<TextFrame> MakeTextFrameSTB(
const std::shared_ptr<TypefaceSTB>& typeface_stb,
Font::Metrics metrics,
const std::string& text);
} // namespace impeller
#endif // FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TEXT_FRAME_STB_H_

View File

@ -1,53 +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/stb/typeface_stb.h"
#include <cstring>
#include "flutter/fml/logging.h"
namespace impeller {
// Instantiate a typeface based on a .ttf or other font file
TypefaceSTB::TypefaceSTB(std::unique_ptr<fml::Mapping> typeface_mapping)
: typeface_mapping_(std::move(typeface_mapping)),
font_info_(std::make_unique<stbtt_fontinfo>()) {
// We need an "offset" into the ttf file
auto offset = stbtt_GetFontOffsetForIndex(typeface_mapping_->GetMapping(), 0);
if (stbtt_InitFont(font_info_.get(), typeface_mapping_->GetMapping(),
offset) == 0) {
FML_LOG(ERROR) << "Failed to initialize stb font from binary data.";
} else {
is_valid_ = true;
}
}
TypefaceSTB::~TypefaceSTB() = default;
bool TypefaceSTB::IsValid() const {
return is_valid_;
}
std::size_t TypefaceSTB::GetHash() const {
if (!IsValid()) {
return 0u;
}
return reinterpret_cast<size_t>(typeface_mapping_->GetMapping());
}
bool TypefaceSTB::IsEqual(const Typeface& other) const {
auto stb_other = reinterpret_cast<const TypefaceSTB*>(&other);
return stb_other->GetHash() == GetHash();
}
const uint8_t* TypefaceSTB::GetTypefaceFile() const {
return typeface_mapping_->GetMapping();
}
const stbtt_fontinfo* TypefaceSTB::GetFontInfo() const {
return font_info_.get();
}
} // namespace impeller

View File

@ -1,50 +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.
#ifndef FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TYPEFACE_STB_H_
#define FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TYPEFACE_STB_H_
#include "flutter/fml/mapping.h"
#include "flutter/third_party/stb/stb_truetype.h"
#include "impeller/base/backend_cast.h"
#include "impeller/typographer/typeface.h"
namespace impeller {
class TypefaceSTB final : public Typeface,
public BackendCast<TypefaceSTB, Typeface> {
public:
// "Typical" conversion from font Points to Pixels.
// This assumes a constant pixels per em.
static constexpr float kPointsToPixels = 96.0 / 72.0;
explicit TypefaceSTB(std::unique_ptr<fml::Mapping> typeface_mapping);
~TypefaceSTB() override;
// |Typeface|
bool IsValid() const override;
// |Comparable<Typeface>|
std::size_t GetHash() const override;
// |Comparable<Typeface>|
bool IsEqual(const Typeface& other) const override;
const uint8_t* GetTypefaceFile() const;
const stbtt_fontinfo* GetFontInfo() const;
private:
std::unique_ptr<fml::Mapping> typeface_mapping_;
std::unique_ptr<stbtt_fontinfo> font_info_;
bool is_valid_ = false;
TypefaceSTB(const TypefaceSTB&) = delete;
TypefaceSTB& operator=(const TypefaceSTB&) = delete;
};
} // namespace impeller
#endif // FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TYPEFACE_STB_H_

View File

@ -1,580 +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/stb/typographer_context_stb.h"
#include <numeric>
#include <utility>
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "fml/closure.h"
#include "fml/mapping.h"
#include "impeller/base/allocation.h"
#include "impeller/core/allocator.h"
#include "impeller/core/device_buffer.h"
#include "impeller/typographer/backends/stb/glyph_atlas_context_stb.h"
#include "impeller/typographer/font_glyph_pair.h"
#include "typeface_stb.h"
#define DISABLE_COLOR_FONT_SUPPORT 1
#ifdef DISABLE_COLOR_FONT_SUPPORT
constexpr auto kColorFontBitsPerPixel = 1;
#else
constexpr auto kColorFontBitsPerPixel = 4;
#endif
namespace impeller {
constexpr size_t kPadding = 1;
std::unique_ptr<TypographerContext> TypographerContextSTB::Make() {
return std::make_unique<TypographerContextSTB>();
}
TypographerContextSTB::TypographerContextSTB() : TypographerContext() {}
TypographerContextSTB::~TypographerContextSTB() = default;
std::shared_ptr<GlyphAtlasContext>
TypographerContextSTB::CreateGlyphAtlasContext(GlyphAtlas::Type type) const {
return std::make_shared<GlyphAtlasContextSTB>(type);
}
// Function returns the count of "remaining pairs" not packed into rect of given
// size.
static size_t PairsFitInAtlasOfSize(
const std::vector<FontGlyphPair>& pairs,
const ISize& atlas_size,
std::vector<Rect>& glyph_positions,
const std::shared_ptr<RectanglePacker>& rect_packer) {
if (atlas_size.IsEmpty()) {
return false;
}
glyph_positions.clear();
glyph_positions.reserve(pairs.size());
size_t i = 0;
for (auto it = pairs.begin(); it != pairs.end(); ++i, ++it) {
const auto& pair = *it;
const Font& font = pair.scaled_font.font;
// We downcast to the correct typeface type to access `stb` specific
// methods.
std::shared_ptr<TypefaceSTB> typeface_stb =
std::reinterpret_pointer_cast<TypefaceSTB>(font.GetTypeface());
// Conversion factor to scale font size in Points to pixels.
// Note this assumes typical DPI.
float text_size_pixels =
font.GetMetrics().point_size * TypefaceSTB::kPointsToPixels;
ISize glyph_size;
{
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
float scale = stbtt_ScaleForMappingEmToPixels(typeface_stb->GetFontInfo(),
text_size_pixels);
stbtt_GetGlyphBitmapBox(typeface_stb->GetFontInfo(),
pair.glyph.glyph.index, scale, scale, &x0, &y0,
&x1, &y1);
glyph_size = ISize(x1 - x0, y1 - y0);
}
IPoint16 location_in_atlas;
if (!rect_packer->AddRect(glyph_size.width + kPadding, //
glyph_size.height + kPadding, //
&location_in_atlas //
)) {
return pairs.size() - i;
}
glyph_positions.emplace_back(Rect::MakeXYWH(location_in_atlas.x(), //
location_in_atlas.y(), //
glyph_size.width, //
glyph_size.height //
));
}
return 0;
}
static bool CanAppendToExistingAtlas(
const std::shared_ptr<GlyphAtlas>& atlas,
const std::vector<FontGlyphPair>& extra_pairs,
std::vector<Rect>& glyph_positions,
ISize atlas_size,
const std::shared_ptr<RectanglePacker>& rect_packer) {
TRACE_EVENT0("impeller", __FUNCTION__);
if (!rect_packer || atlas_size.IsEmpty()) {
return false;
}
// We assume that all existing glyphs will fit. After all, they fit before.
// The glyph_positions only contains the values for the additional glyphs
// from extra_pairs.
FML_DCHECK(glyph_positions.size() == 0);
glyph_positions.reserve(extra_pairs.size());
for (size_t i = 0; i < extra_pairs.size(); i++) {
const FontGlyphPair& pair = extra_pairs[i];
const Font& font = pair.scaled_font.font;
// We downcast to the correct typeface type to access `stb` specific methods
std::shared_ptr<TypefaceSTB> typeface_stb =
std::reinterpret_pointer_cast<TypefaceSTB>(font.GetTypeface());
// Conversion factor to scale font size in Points to pixels.
// Note this assumes typical DPI.
float text_size_pixels =
font.GetMetrics().point_size * TypefaceSTB::kPointsToPixels;
ISize glyph_size;
{
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
float scale_y = stbtt_ScaleForMappingEmToPixels(
typeface_stb->GetFontInfo(), text_size_pixels);
float scale_x = scale_y;
stbtt_GetGlyphBitmapBox(typeface_stb->GetFontInfo(),
pair.glyph.glyph.index, scale_x, scale_y, &x0,
&y0, &x1, &y1);
glyph_size = ISize(x1 - x0, y1 - y0);
}
IPoint16 location_in_atlas;
if (!rect_packer->AddRect(glyph_size.width + kPadding, //
glyph_size.height + kPadding, //
&location_in_atlas //
)) {
return false;
}
glyph_positions.emplace_back(Rect::MakeXYWH(location_in_atlas.x(), //
location_in_atlas.y(), //
glyph_size.width, //
glyph_size.height //
));
}
return true;
}
static ISize OptimumAtlasSizeForFontGlyphPairs(
const std::vector<FontGlyphPair>& pairs,
std::vector<Rect>& glyph_positions,
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
GlyphAtlas::Type type,
const ISize& max_texture_size) {
static constexpr auto kMinAtlasSize = 8u;
static constexpr auto kMinAlphaBitmapSize = 1024u;
TRACE_EVENT0("impeller", __FUNCTION__);
ISize current_size = type == GlyphAtlas::Type::kAlphaBitmap
? ISize(kMinAlphaBitmapSize, kMinAlphaBitmapSize)
: ISize(kMinAtlasSize, kMinAtlasSize);
size_t total_pairs = pairs.size() + 1;
do {
auto rect_packer = std::shared_ptr<RectanglePacker>(
RectanglePacker::Factory(current_size.width, current_size.height));
auto remaining_pairs = PairsFitInAtlasOfSize(pairs, current_size,
glyph_positions, rect_packer);
if (remaining_pairs == 0) {
atlas_context->UpdateRectPacker(rect_packer);
return current_size;
} else if (remaining_pairs < std::ceil(total_pairs / 2)) {
current_size = ISize::MakeWH(
std::max(current_size.width, current_size.height),
Allocation::NextPowerOfTwoSize(
std::min(current_size.width, current_size.height) + 1));
} else {
current_size = ISize::MakeWH(
Allocation::NextPowerOfTwoSize(current_size.width + 1),
Allocation::NextPowerOfTwoSize(current_size.height + 1));
}
} while (current_size.width <= max_texture_size.width &&
current_size.height <= max_texture_size.height);
return ISize{0, 0};
}
static void DrawGlyph(BitmapSTB* bitmap,
const ScaledFont& scaled_font,
const Glyph& glyph,
const Rect& location,
bool has_color) {
const auto& metrics = scaled_font.font.GetMetrics();
const impeller::Font& font = scaled_font.font;
auto typeface = font.GetTypeface();
// We downcast to the correct typeface type to access `stb` specific methods
std::shared_ptr<TypefaceSTB> typeface_stb =
std::reinterpret_pointer_cast<TypefaceSTB>(typeface);
// Conversion factor to scale font size in Points to pixels.
// Note this assumes typical DPI.
float text_size_pixels = metrics.point_size * TypefaceSTB::kPointsToPixels;
float scale_y = stbtt_ScaleForMappingEmToPixels(typeface_stb->GetFontInfo(),
text_size_pixels);
float scale_x = scale_y;
auto output = bitmap->GetPixelAddress({static_cast<size_t>(location.GetX()),
static_cast<size_t>(location.GetY())});
// For Alpha and Signed Distance field bitmaps we can use STB to draw the
// Glyph in place
if (!has_color || DISABLE_COLOR_FONT_SUPPORT) {
stbtt_MakeGlyphBitmap(typeface_stb->GetFontInfo(), output,
location.GetWidth(), location.GetHeight(),
bitmap->GetRowBytes(), scale_x, scale_y, glyph.index);
} else {
// But for color bitmaps we need to get the glyph pixels and then carry all
// channels into the atlas bitmap. This may not be performant but I'm unsure
// of any other approach currently.
int glyph_bitmap_width = 0;
int glyph_bitmap_height = 0;
int glyph_bitmap_xoff = 0;
int glyph_bitmap_yoff = 0;
auto glyph_pixels = stbtt_GetGlyphBitmap(
typeface_stb->GetFontInfo(), scale_x, scale_y, glyph.index,
&glyph_bitmap_width, &glyph_bitmap_height, &glyph_bitmap_xoff,
&glyph_bitmap_yoff);
uint8_t* write_pos = output;
for (auto y = 0; y < glyph_bitmap_height; ++y) {
for (auto x = 0; x < glyph_bitmap_width; ++x) {
// Color bitmaps write as White (i.e. what is 0 in an alpha bitmap is
// 255 in a color bitmap) But not alpha. Alpha still carries
// transparency info in the normal way.
// There's some issue with color fonts, in that if the pixel color is
// nonzero, the alpha is ignored during rendering. That is, partially
// (or fully) transparent pixels with nonzero color are rendered as
// fully opaque.
uint8_t a = glyph_pixels[x + y * glyph_bitmap_width];
uint8_t c = 255 - a;
// Red channel
*write_pos = c;
write_pos++;
// Green channel
*write_pos = c;
write_pos++;
// Blue channel
*write_pos = c;
write_pos++;
// Alpha channel
*write_pos = a;
write_pos++;
}
// next row
write_pos = output + (y * bitmap->GetRowBytes());
}
stbtt_FreeBitmap(glyph_pixels, nullptr);
}
}
static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
const std::shared_ptr<BitmapSTB>& bitmap,
const std::vector<FontGlyphPair>& new_pairs) {
TRACE_EVENT0("impeller", __FUNCTION__);
FML_DCHECK(bitmap != nullptr);
bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
for (const FontGlyphPair& pair : new_pairs) {
auto pos = atlas.FindFontGlyphBounds(pair);
if (!pos.has_value()) {
continue;
}
DrawGlyph(bitmap.get(), pair.scaled_font, pair.glyph.glyph,
pos.value().first, has_color);
}
return true;
}
static std::shared_ptr<BitmapSTB> CreateAtlasBitmap(const GlyphAtlas& atlas,
const ISize& atlas_size) {
TRACE_EVENT0("impeller", __FUNCTION__);
size_t bytes_per_pixel = 1;
if (atlas.GetType() == GlyphAtlas::Type::kColorBitmap &&
!DISABLE_COLOR_FONT_SUPPORT) {
bytes_per_pixel = kColorFontBitsPerPixel;
}
auto bitmap = std::make_shared<BitmapSTB>(atlas_size.width, atlas_size.height,
bytes_per_pixel);
bool has_color = atlas.GetType() == GlyphAtlas::Type::kColorBitmap;
atlas.IterateGlyphs([&bitmap, has_color](const ScaledFont& scaled_font,
const SubpixelGlyph& glyph,
const Rect& location) -> bool {
DrawGlyph(bitmap.get(), scaled_font, glyph.glyph, location, has_color);
return true;
});
return bitmap;
}
// static bool UpdateGlyphTextureAtlas(std::shared_ptr<SkBitmap> bitmap,
static bool UpdateGlyphTextureAtlas(std::shared_ptr<BitmapSTB>& bitmap,
const std::shared_ptr<Allocator>& allocator,
std::shared_ptr<BlitPass>& blit_pass,
const std::shared_ptr<Texture>& texture) {
TRACE_EVENT0("impeller", __FUNCTION__);
FML_DCHECK(bitmap != nullptr);
auto texture_descriptor = texture->GetTextureDescriptor();
auto mapping = std::make_shared<fml::NonOwnedMapping>(
reinterpret_cast<const uint8_t*>(bitmap->GetPixels()), // data
texture_descriptor.GetByteSizeOfBaseMipLevel() // size
// As the bitmap is static in this module I believe we don't need to
// specify a release proc.
);
std::shared_ptr<DeviceBuffer> device_buffer =
allocator->CreateBufferWithCopy(*mapping);
blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), texture);
return blit_pass->EncodeCommands(allocator);
}
static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
const std::shared_ptr<Allocator>& allocator,
std::shared_ptr<BlitPass>& blit_pass,
std::shared_ptr<BitmapSTB>& bitmap,
const ISize& atlas_size,
PixelFormat format) {
TRACE_EVENT0("impeller", __FUNCTION__);
if (!allocator) {
return nullptr;
}
FML_DCHECK(bitmap != nullptr);
TextureDescriptor texture_descriptor;
texture_descriptor.storage_mode = StorageMode::kDevicePrivate;
texture_descriptor.format = format;
texture_descriptor.size = atlas_size;
if (bitmap->GetRowBytes() * bitmap->GetHeight() !=
texture_descriptor.GetByteSizeOfBaseMipLevel()) {
return nullptr;
}
auto texture = allocator->CreateTexture(texture_descriptor);
if (!texture || !texture->IsValid()) {
return nullptr;
}
texture->SetLabel("GlyphAtlas");
std::shared_ptr<fml::NonOwnedMapping> mapping =
std::make_shared<fml::NonOwnedMapping>(
reinterpret_cast<const uint8_t*>(bitmap->GetPixels()), // data
texture_descriptor.GetByteSizeOfBaseMipLevel() // size
// As the bitmap is static in this module I believe we don't need to
// specify a release proc.
);
std::shared_ptr<DeviceBuffer> device_buffer =
allocator->CreateBufferWithCopy(*mapping);
blit_pass->AddCopy(DeviceBuffer::AsBufferView(device_buffer), texture);
if (!blit_pass->EncodeCommands(allocator)) {
return nullptr;
}
return texture;
}
static Rect ComputeGlyphSize(const ScaledFont& font,
const SubpixelGlyph& glyph) {
std::shared_ptr<TypefaceSTB> typeface_stb =
std::reinterpret_pointer_cast<TypefaceSTB>(font.font.GetTypeface());
float scale = stbtt_ScaleForMappingEmToPixels(
typeface_stb->GetFontInfo(),
font.font.GetMetrics().point_size * TypefaceSTB::kPointsToPixels);
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
stbtt_GetGlyphBitmapBox(typeface_stb->GetFontInfo(), glyph.glyph.index, scale,
scale, &x0, &y0, &x1, &y1);
return Rect::MakeLTRB(0, 0, x1 - x0, y1 - y0);
}
std::shared_ptr<GlyphAtlas> TypographerContextSTB::CreateGlyphAtlas(
Context& context,
GlyphAtlas::Type type,
HostBuffer& host_buffer,
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
const FontGlyphMap& font_glyph_map) const {
TRACE_EVENT0("impeller", __FUNCTION__);
if (!IsValid()) {
return nullptr;
}
auto& atlas_context_stb = GlyphAtlasContextSTB::Cast(*atlas_context);
std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
if (font_glyph_map.empty()) {
return last_atlas;
}
// ---------------------------------------------------------------------------
// Step 1: Determine if the atlas type and font glyph pairs are compatible
// with the current atlas and reuse if possible.
// ---------------------------------------------------------------------------
std::vector<FontGlyphPair> new_glyphs;
std::vector<Rect> new_sizes;
for (const auto& font_value : font_glyph_map) {
const ScaledFont& scaled_font = font_value.first;
const FontGlyphAtlas* font_glyph_atlas =
last_atlas->GetFontGlyphAtlas(scaled_font.font, scaled_font.scale);
if (font_glyph_atlas) {
for (const SubpixelGlyph& glyph : font_value.second) {
if (!font_glyph_atlas->FindGlyphBounds(glyph)) {
new_glyphs.emplace_back(scaled_font, glyph);
new_sizes.push_back(ComputeGlyphSize(scaled_font, glyph));
}
}
} else {
for (const SubpixelGlyph& glyph : font_value.second) {
new_glyphs.emplace_back(scaled_font, glyph);
new_sizes.push_back(ComputeGlyphSize(scaled_font, glyph));
}
}
}
if (last_atlas->GetType() == type && new_glyphs.size() == 0) {
return last_atlas;
}
std::shared_ptr<CommandBuffer> cmd_buffer = context.CreateCommandBuffer();
std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
fml::ScopedCleanupClosure closure([&cmd_buffer, &context]() {
context.GetCommandQueue()->Submit({std::move(cmd_buffer)});
});
// ---------------------------------------------------------------------------
// 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.
// ---------------------------------------------------------------------------
std::vector<Rect> glyph_positions;
if (last_atlas->GetType() == type &&
CanAppendToExistingAtlas(last_atlas, new_glyphs, glyph_positions,
atlas_context->GetAtlasSize(),
atlas_context->GetRectPacker())) {
// The old bitmap will be reused and only the additional glyphs will be
// 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++) {
last_atlas->AddTypefaceGlyphPositionAndBounds(
new_glyphs[i], glyph_positions[i], new_sizes[i]);
}
// ---------------------------------------------------------------------------
// Step 4a: Draw new font-glyph pairs into the existing bitmap.
// ---------------------------------------------------------------------------
// auto bitmap = atlas_context->GetBitmap();
auto bitmap = atlas_context_stb.GetBitmap();
if (!UpdateAtlasBitmap(*last_atlas, bitmap, new_glyphs)) {
return nullptr;
}
// ---------------------------------------------------------------------------
// Step 5a: Update the existing texture with the updated bitmap.
// ---------------------------------------------------------------------------
if (!UpdateGlyphTextureAtlas(bitmap, context.GetResourceAllocator(),
blit_pass, last_atlas->GetTexture())) {
return nullptr;
}
return last_atlas;
}
// A new glyph atlas must be created.
// ---------------------------------------------------------------------------
// Step 3b: Get the optimum size of the texture atlas.
// ---------------------------------------------------------------------------
std::vector<FontGlyphPair> font_glyph_pairs;
font_glyph_pairs.reserve(std::accumulate(
font_glyph_map.begin(), font_glyph_map.end(), 0,
[](const int a, const auto& b) { return a + b.second.size(); }));
for (const auto& font_value : font_glyph_map) {
const ScaledFont& scaled_font = font_value.first;
for (const SubpixelGlyph& glyph : font_value.second) {
font_glyph_pairs.push_back({scaled_font, glyph});
}
}
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
font_glyph_pairs, //
glyph_positions, //
atlas_context, //
type, //
context.GetResourceAllocator()->GetMaxTextureSizeSupported() //
);
atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size, 0);
if (atlas_size.IsEmpty()) {
return nullptr;
}
// ---------------------------------------------------------------------------
// 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.
// ---------------------------------------------------------------------------
if (glyph_positions.size() != font_glyph_pairs.size()) {
return nullptr;
}
// ---------------------------------------------------------------------------
// Step 5b: Record the positions in the glyph atlas.
// ---------------------------------------------------------------------------
{
size_t i = 0;
for (auto it = font_glyph_pairs.begin(); it != font_glyph_pairs.end();
++i, ++it) {
glyph_atlas->AddTypefaceGlyphPositionAndBounds(*it, glyph_positions[i],
new_sizes[i]);
}
}
// ---------------------------------------------------------------------------
// Step 6b: Draw font-glyph pairs in the correct spot in the atlas.
// ---------------------------------------------------------------------------
auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
if (!bitmap) {
return nullptr;
}
atlas_context_stb.UpdateBitmap(bitmap);
// ---------------------------------------------------------------------------
// Step 7b: Upload the atlas as a texture.
// ---------------------------------------------------------------------------
PixelFormat format;
switch (type) {
case GlyphAtlas::Type::kAlphaBitmap:
format = context.GetCapabilities()->GetDefaultGlyphAtlasFormat();
break;
case GlyphAtlas::Type::kColorBitmap:
format = DISABLE_COLOR_FONT_SUPPORT
? context.GetCapabilities()->GetDefaultGlyphAtlasFormat()
: PixelFormat::kR8G8B8A8UNormInt;
break;
}
auto texture = UploadGlyphTextureAtlas(context.GetResourceAllocator(),
blit_pass, bitmap, atlas_size, format);
if (!texture) {
return nullptr;
}
// ---------------------------------------------------------------------------
// Step 8b: Record the texture in the glyph atlas.
// ---------------------------------------------------------------------------
glyph_atlas->SetTexture(std::move(texture));
return glyph_atlas;
}
} // namespace impeller

View File

@ -1,43 +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.
#ifndef FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TYPOGRAPHER_CONTEXT_STB_H_
#define FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TYPOGRAPHER_CONTEXT_STB_H_
#include "impeller/typographer/glyph_atlas.h"
#include "impeller/typographer/typographer_context.h"
#include <memory>
namespace impeller {
class TypographerContextSTB : public TypographerContext {
public:
static std::unique_ptr<TypographerContext> Make();
TypographerContextSTB();
~TypographerContextSTB() override;
// |TypographerContext|
std::shared_ptr<GlyphAtlasContext> CreateGlyphAtlasContext(
GlyphAtlas::Type type) const override;
// |TypographerContext|
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
Context& context,
GlyphAtlas::Type type,
HostBuffer& host_buffer,
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
const FontGlyphMap& font_glyph_map) const override;
private:
TypographerContextSTB(const TypographerContextSTB&) = delete;
TypographerContextSTB& operator=(const TypographerContextSTB&) = delete;
};
} // namespace impeller
#endif // FLUTTER_IMPELLER_TYPOGRAPHER_BACKENDS_STB_TYPOGRAPHER_CONTEXT_STB_H_

View File

@ -445,9 +445,6 @@ impeller_Play_AiksTest_CanRenderSweepGradientRepeat_Vulkan.png
impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringEnabled_Metal.png
impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringEnabled_OpenGLES.png
impeller_Play_AiksTest_CanRenderSweepGradientWithDitheringEnabled_Vulkan.png
impeller_Play_AiksTest_CanRenderTextFrameSTB_Metal.png
impeller_Play_AiksTest_CanRenderTextFrameSTB_OpenGLES.png
impeller_Play_AiksTest_CanRenderTextFrameSTB_Vulkan.png
impeller_Play_AiksTest_CanRenderTextFrameWithFractionScaling_Metal.png
impeller_Play_AiksTest_CanRenderTextFrameWithFractionScaling_OpenGLES.png
impeller_Play_AiksTest_CanRenderTextFrameWithFractionScaling_Vulkan.png