mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Append to existing atlas if room exists, reuse texture (flutter/engine#38253)
* [impeller][wip] append to existing atlas if room exists * cleanups and reuse texture * ++ * ++ * ++ * ++ * dnfield review * ++ * ++ * ++
This commit is contained in:
parent
8687f16411
commit
b2a0500a9e
@ -22,6 +22,11 @@
|
||||
|
||||
namespace impeller {
|
||||
|
||||
// TODO(bdero): We might be able to remove this per-glyph padding if we fix
|
||||
// the underlying causes of the overlap.
|
||||
// https://github.com/flutter/flutter/issues/114563
|
||||
constexpr auto kPadding = 2;
|
||||
|
||||
TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr<Context> context)
|
||||
: TextRenderContext(std::move(context)) {}
|
||||
|
||||
@ -58,33 +63,27 @@ static FontGlyphPair::Vector CollectUniqueFontGlyphPairs(
|
||||
return vector;
|
||||
}
|
||||
|
||||
static size_t PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs,
|
||||
const ISize& atlas_size,
|
||||
std::vector<Rect>& glyph_positions) {
|
||||
static size_t PairsFitInAtlasOfSize(
|
||||
const FontGlyphPair::Vector& pairs,
|
||||
const ISize& atlas_size,
|
||||
std::vector<Rect>& glyph_positions,
|
||||
const std::shared_ptr<GrRectanizer>& rect_packer) {
|
||||
if (atlas_size.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto rect_packer = std::unique_ptr<GrRectanizer>(
|
||||
GrRectanizer::Factory(atlas_size.width, atlas_size.height));
|
||||
|
||||
glyph_positions.clear();
|
||||
glyph_positions.reserve(pairs.size());
|
||||
|
||||
// TODO(bdero): We might be able to remove this per-glyph padding if we fix
|
||||
// the underlying causes of the overlap.
|
||||
// https://github.com/flutter/flutter/issues/114563
|
||||
constexpr auto padding = 2;
|
||||
|
||||
for (size_t i = 0; i < pairs.size(); i++) {
|
||||
const auto& pair = pairs[i];
|
||||
|
||||
const auto glyph_size =
|
||||
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
|
||||
SkIPoint16 location_in_atlas;
|
||||
if (!rect_packer->addRect(glyph_size.width + padding, //
|
||||
glyph_size.height + padding, //
|
||||
&location_in_atlas //
|
||||
if (!rect_packer->addRect(glyph_size.width + kPadding, //
|
||||
glyph_size.height + kPadding, //
|
||||
&location_in_atlas //
|
||||
)) {
|
||||
return pairs.size() - i;
|
||||
}
|
||||
@ -98,9 +97,48 @@ static size_t PairsFitInAtlasOfSize(const FontGlyphPair::Vector& pairs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool CanAppendToExistingAtlas(
|
||||
const std::shared_ptr<GlyphAtlas>& atlas,
|
||||
const FontGlyphPair::Vector& extra_pairs,
|
||||
std::vector<Rect>& glyph_positions,
|
||||
ISize atlas_size,
|
||||
const std::shared_ptr<GrRectanizer>& 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 auto& pair = extra_pairs[i];
|
||||
|
||||
const auto glyph_size =
|
||||
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
|
||||
SkIPoint16 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 FontGlyphPair::Vector& pairs,
|
||||
std::vector<Rect>& glyph_positions) {
|
||||
std::vector<Rect>& glyph_positions,
|
||||
const std::shared_ptr<GlyphAtlasContext>& atlas_context) {
|
||||
static constexpr auto kMinAtlasSize = 8u;
|
||||
static constexpr auto kMaxAtlasSize = 4096u;
|
||||
|
||||
@ -109,9 +147,13 @@ static ISize OptimumAtlasSizeForFontGlyphPairs(
|
||||
ISize current_size(kMinAtlasSize, kMinAtlasSize);
|
||||
size_t total_pairs = pairs.size() + 1;
|
||||
do {
|
||||
auto remaining_pairs =
|
||||
PairsFitInAtlasOfSize(pairs, current_size, glyph_positions);
|
||||
auto rect_packer = std::shared_ptr<GrRectanizer>(
|
||||
GrRectanizer::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(
|
||||
@ -243,6 +285,59 @@ static void ConvertBitmapToSignedDistanceField(uint8_t* pixels,
|
||||
#undef nearestpt
|
||||
}
|
||||
|
||||
static void DrawGlyph(SkCanvas* canvas,
|
||||
const FontGlyphPair& font_glyph,
|
||||
const Rect& location) {
|
||||
const auto& metrics = font_glyph.font.GetMetrics();
|
||||
const auto position = SkPoint::Make(location.origin.x / metrics.scale,
|
||||
location.origin.y / metrics.scale);
|
||||
SkGlyphID glyph_id = font_glyph.glyph.index;
|
||||
|
||||
SkFont sk_font(
|
||||
TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(),
|
||||
metrics.point_size);
|
||||
auto glyph_color = SK_ColorWHITE;
|
||||
|
||||
SkPaint glyph_paint;
|
||||
glyph_paint.setColor(glyph_color);
|
||||
canvas->resetMatrix();
|
||||
canvas->scale(metrics.scale, metrics.scale);
|
||||
canvas->drawGlyphs(
|
||||
1u, // count
|
||||
&glyph_id, // glyphs
|
||||
&position, // positions
|
||||
SkPoint::Make(-font_glyph.glyph.bounds.GetLeft(),
|
||||
-font_glyph.glyph.bounds.GetTop()), // origin
|
||||
sk_font, // font
|
||||
glyph_paint // paint
|
||||
);
|
||||
}
|
||||
|
||||
static bool UpdateAtlasBitmap(const GlyphAtlas& atlas,
|
||||
const std::shared_ptr<SkBitmap>& bitmap,
|
||||
const FontGlyphPair::Vector& new_pairs) {
|
||||
TRACE_EVENT0("impeller", __FUNCTION__);
|
||||
FML_DCHECK(bitmap != nullptr);
|
||||
|
||||
auto surface = SkSurface::MakeRasterDirect(bitmap->pixmap());
|
||||
if (!surface) {
|
||||
return false;
|
||||
}
|
||||
auto canvas = surface->getCanvas();
|
||||
if (!canvas) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& pair : new_pairs) {
|
||||
auto pos = atlas.FindFontGlyphPosition(pair);
|
||||
if (!pos.has_value()) {
|
||||
continue;
|
||||
}
|
||||
DrawGlyph(canvas, pair, pos.value());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
|
||||
const ISize& atlas_size) {
|
||||
TRACE_EVENT0("impeller", __FUNCTION__);
|
||||
@ -263,6 +358,7 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
|
||||
if (!bitmap->tryAllocPixels(image_info)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto surface = SkSurface::MakeRasterDirect(bitmap->pixmap());
|
||||
if (!surface) {
|
||||
return nullptr;
|
||||
@ -272,37 +368,31 @@ static std::shared_ptr<SkBitmap> CreateAtlasBitmap(const GlyphAtlas& atlas,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
atlas.IterateGlyphs([canvas](const FontGlyphPair& font_glyph,
|
||||
const Rect& location) -> bool {
|
||||
const auto& metrics = font_glyph.font.GetMetrics();
|
||||
const auto position = SkPoint::Make(location.origin.x / metrics.scale,
|
||||
location.origin.y / metrics.scale);
|
||||
SkGlyphID glyph_id = font_glyph.glyph.index;
|
||||
|
||||
SkFont sk_font(
|
||||
TypefaceSkia::Cast(*font_glyph.font.GetTypeface()).GetSkiaTypeface(),
|
||||
metrics.point_size);
|
||||
auto glyph_color = SK_ColorWHITE;
|
||||
|
||||
SkPaint glyph_paint;
|
||||
glyph_paint.setColor(glyph_color);
|
||||
canvas->resetMatrix();
|
||||
canvas->scale(metrics.scale, metrics.scale);
|
||||
canvas->drawGlyphs(
|
||||
1u, // count
|
||||
&glyph_id, // glyphs
|
||||
&position, // positions
|
||||
SkPoint::Make(-font_glyph.glyph.bounds.GetLeft(),
|
||||
-font_glyph.glyph.bounds.GetTop()), // origin
|
||||
sk_font, // font
|
||||
glyph_paint // paint
|
||||
);
|
||||
return true;
|
||||
});
|
||||
atlas.IterateGlyphs(
|
||||
[canvas](const FontGlyphPair& font_glyph, const Rect& location) -> bool {
|
||||
DrawGlyph(canvas, font_glyph, location);
|
||||
return true;
|
||||
});
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
static bool UpdateGlyphTextureAtlas(std::shared_ptr<SkBitmap> bitmap,
|
||||
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->getAddr(0, 0)), // data
|
||||
texture_descriptor.GetByteSizeOfBaseMipLevel(), // size
|
||||
[bitmap](auto, auto) mutable { bitmap.reset(); } // proc
|
||||
);
|
||||
|
||||
return texture->SetContents(mapping);
|
||||
}
|
||||
|
||||
static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
|
||||
const std::shared_ptr<Allocator>& allocator,
|
||||
std::shared_ptr<SkBitmap> bitmap,
|
||||
@ -367,26 +457,61 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
// Step 2: Determine if the atlas type and font glyph pairs are compatible
|
||||
// with the current atlas and reuse if possible.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (last_atlas->GetType() == type &&
|
||||
last_atlas->HasSamePairs(font_glyph_pairs)) {
|
||||
auto new_glyphs = last_atlas->HasSamePairs(font_glyph_pairs);
|
||||
if (last_atlas->GetType() == type && new_glyphs.size() == 0) {
|
||||
return last_atlas;
|
||||
}
|
||||
|
||||
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
|
||||
atlas_context->UpdateGlyphAtlas(glyph_atlas);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 3: Get the optimum size of the texture atlas.
|
||||
// Step 3: Determine if the additional missing glyphs can be appended to the
|
||||
// existing bitmap without recreating the atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
std::vector<Rect> glyph_positions;
|
||||
const auto atlas_size =
|
||||
OptimumAtlasSizeForFontGlyphPairs(font_glyph_pairs, glyph_positions);
|
||||
if (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 4: 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->AddTypefaceGlyphPosition(new_glyphs[i], glyph_positions[i]);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 5: Draw new font-glyph pairs into the existing bitmap.
|
||||
// ---------------------------------------------------------------------------
|
||||
auto bitmap = atlas_context->GetBitmap();
|
||||
if (!UpdateAtlasBitmap(*last_atlas, bitmap, new_glyphs)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 6: Update the existing texture with the updated bitmap.
|
||||
// ---------------------------------------------------------------------------
|
||||
if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) {
|
||||
return nullptr;
|
||||
}
|
||||
return last_atlas;
|
||||
}
|
||||
// A new glyph atlas must be created.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 4: Get the optimum size of the texture atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
|
||||
auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
|
||||
font_glyph_pairs, glyph_positions, atlas_context);
|
||||
|
||||
atlas_context->UpdateGlyphAtlas(glyph_atlas, atlas_size);
|
||||
if (atlas_size.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 4: Find location of font-glyph pairs in the atlas. We have this from
|
||||
// Step 5: 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.
|
||||
@ -396,7 +521,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 5: Record the positions in the glyph atlas.
|
||||
// Step 6: Record the positions in the glyph atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
for (size_t i = 0, count = glyph_positions.size(); i < count; i++) {
|
||||
glyph_atlas->AddTypefaceGlyphPosition(font_glyph_pairs[i],
|
||||
@ -404,15 +529,16 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 6: Draw font-glyph pairs in the correct spot in the atlas.
|
||||
// Step 7: Draw font-glyph pairs in the correct spot in the atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
|
||||
if (!bitmap) {
|
||||
return nullptr;
|
||||
}
|
||||
atlas_context->UpdateBitmap(bitmap);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 7: Upload the atlas as a texture.
|
||||
// Step 8: Upload the atlas as a texture.
|
||||
// ---------------------------------------------------------------------------
|
||||
PixelFormat format;
|
||||
switch (type) {
|
||||
@ -434,7 +560,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Step 8: Record the texture in the glyph atlas.
|
||||
// Step 9: Record the texture in the glyph atlas.
|
||||
// ---------------------------------------------------------------------------
|
||||
glyph_atlas->SetTexture(std::move(texture));
|
||||
|
||||
|
||||
@ -9,7 +9,8 @@
|
||||
namespace impeller {
|
||||
|
||||
GlyphAtlasContext::GlyphAtlasContext()
|
||||
: atlas_(std::make_shared<GlyphAtlas>(GlyphAtlas::Type::kAlphaBitmap)) {}
|
||||
: atlas_(std::make_shared<GlyphAtlas>(GlyphAtlas::Type::kAlphaBitmap)),
|
||||
atlas_size_(ISize(0, 0)) {}
|
||||
|
||||
GlyphAtlasContext::~GlyphAtlasContext() {}
|
||||
|
||||
@ -17,8 +18,31 @@ std::shared_ptr<GlyphAtlas> GlyphAtlasContext::GetGlyphAtlas() const {
|
||||
return atlas_;
|
||||
}
|
||||
|
||||
void GlyphAtlasContext::UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas) {
|
||||
const ISize& GlyphAtlasContext::GetAtlasSize() const {
|
||||
return atlas_size_;
|
||||
}
|
||||
|
||||
std::shared_ptr<SkBitmap> GlyphAtlasContext::GetBitmap() const {
|
||||
return bitmap_;
|
||||
}
|
||||
|
||||
std::shared_ptr<skgpu::Rectanizer> GlyphAtlasContext::GetRectPacker() const {
|
||||
return rect_packer_;
|
||||
}
|
||||
|
||||
void GlyphAtlasContext::UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas,
|
||||
ISize size) {
|
||||
atlas_ = std::move(atlas);
|
||||
atlas_size_ = size;
|
||||
}
|
||||
|
||||
void GlyphAtlasContext::UpdateBitmap(std::shared_ptr<SkBitmap> bitmap) {
|
||||
bitmap_ = std::move(bitmap);
|
||||
}
|
||||
|
||||
void GlyphAtlasContext::UpdateRectPacker(
|
||||
std::shared_ptr<skgpu::Rectanizer> rect_packer) {
|
||||
rect_packer_ = std::move(rect_packer);
|
||||
}
|
||||
|
||||
GlyphAtlas::GlyphAtlas(Type type) : type_(type) {}
|
||||
@ -76,13 +100,15 @@ size_t GlyphAtlas::IterateGlyphs(
|
||||
return count;
|
||||
}
|
||||
|
||||
bool GlyphAtlas::HasSamePairs(const FontGlyphPair::Vector& new_glyphs) {
|
||||
for (const auto& pair : new_glyphs) {
|
||||
FontGlyphPair::Vector GlyphAtlas::HasSamePairs(
|
||||
const FontGlyphPair::Vector& new_glyphs) {
|
||||
std::vector<FontGlyphPair> new_pairs;
|
||||
for (auto pair : new_glyphs) {
|
||||
if (positions_.find(pair) == positions_.end()) {
|
||||
return false;
|
||||
new_pairs.push_back(pair);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return new_pairs;
|
||||
}
|
||||
|
||||
} // namespace impeller
|
||||
|
||||
@ -15,6 +15,11 @@
|
||||
#include "impeller/renderer/texture.h"
|
||||
#include "impeller/typographer/font_glyph_pair.h"
|
||||
|
||||
class SkBitmap;
|
||||
namespace skgpu {
|
||||
class Rectanizer;
|
||||
}
|
||||
|
||||
namespace impeller {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -122,9 +127,10 @@ class GlyphAtlas {
|
||||
///
|
||||
/// @param[in] new_glyphs The full set of new glyphs
|
||||
///
|
||||
/// @return Whether this atlas contains all passed pairs.
|
||||
/// @return A vector containing the glyphs from new_glyphs that are not
|
||||
/// present in the existing atlas. May be empty of there are none.
|
||||
///
|
||||
bool HasSamePairs(const FontGlyphPair::Vector& new_glyphs);
|
||||
FontGlyphPair::Vector HasSamePairs(const FontGlyphPair::Vector& new_glyphs);
|
||||
|
||||
private:
|
||||
const Type type_;
|
||||
@ -152,12 +158,31 @@ class GlyphAtlasContext {
|
||||
/// @brief Retrieve the current glyph atlas.
|
||||
std::shared_ptr<GlyphAtlas> GetGlyphAtlas() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Retrieve the size of the current glyph atlas.
|
||||
const ISize& GetAtlasSize() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Retrieve the previous (if any) SkBitmap instance.
|
||||
std::shared_ptr<SkBitmap> GetBitmap() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Retrieve the previous (if any) rect packer.
|
||||
std::shared_ptr<skgpu::Rectanizer> GetRectPacker() const;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Update the context with a newly constructed glyph atlas.
|
||||
void UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas);
|
||||
void UpdateGlyphAtlas(std::shared_ptr<GlyphAtlas> atlas, ISize size);
|
||||
|
||||
void UpdateBitmap(std::shared_ptr<SkBitmap> bitmap);
|
||||
|
||||
void UpdateRectPacker(std::shared_ptr<skgpu::Rectanizer> rect_packer);
|
||||
|
||||
private:
|
||||
std::shared_ptr<GlyphAtlas> atlas_;
|
||||
ISize atlas_size_;
|
||||
std::shared_ptr<SkBitmap> bitmap_;
|
||||
std::shared_ptr<skgpu::Rectanizer> rect_packer_;
|
||||
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(GlyphAtlasContext);
|
||||
};
|
||||
|
||||
@ -168,5 +168,38 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
|
||||
atlas->GetTexture()->GetSize().height);
|
||||
}
|
||||
|
||||
TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
|
||||
auto context = TextRenderContext::Create(GetContext());
|
||||
auto atlas_context = std::make_shared<GlyphAtlasContext>();
|
||||
ASSERT_TRUE(context && context->IsValid());
|
||||
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 old_packer = atlas_context->GetRectPacker();
|
||||
|
||||
ASSERT_NE(atlas, nullptr);
|
||||
ASSERT_NE(atlas->GetTexture(), nullptr);
|
||||
ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
|
||||
|
||||
auto* first_texture = atlas->GetTexture().get();
|
||||
|
||||
// now create a new glyph atlas with a nearly identical blob.
|
||||
|
||||
auto blob2 = SkTextBlob::MakeFromString("spooky 2", sk_font);
|
||||
auto next_atlas =
|
||||
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
|
||||
TextFrameFromTextBlob(blob2));
|
||||
ASSERT_EQ(atlas, next_atlas);
|
||||
auto* second_texture = next_atlas->GetTexture().get();
|
||||
|
||||
auto new_packer = atlas_context->GetRectPacker();
|
||||
|
||||
ASSERT_EQ(second_texture, first_texture);
|
||||
ASSERT_EQ(old_packer, new_packer);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace impeller
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user