14 #include "flutter/fml/logging.h"
15 #include "flutter/fml/trace_event.h"
16 #include "fml/closure.h"
36 #include "include/core/SkColor.h"
37 #include "include/core/SkImageInfo.h"
38 #include "include/core/SkPaint.h"
39 #include "include/core/SkSize.h"
41 #include "third_party/skia/include/core/SkBitmap.h"
42 #include "third_party/skia/include/core/SkBlendMode.h"
43 #include "third_party/skia/include/core/SkCanvas.h"
44 #include "third_party/skia/include/core/SkFont.h"
45 #include "third_party/skia/include/core/SkSurface.h"
55 return SkPaint::Cap::kButt_Cap;
57 return SkPaint::Cap::kRound_Cap;
59 return SkPaint::Cap::kSquare_Cap;
67 return SkPaint::Join::kMiter_Join;
69 return SkPaint::Join::kRound_Join;
71 return SkPaint::Join::kBevel_Join;
78 return std::make_shared<TypographerContextSkia>();
85 std::shared_ptr<GlyphAtlasContext>
87 return std::make_shared<GlyphAtlasContext>(
type);
93 return SkImageInfo::MakeA8(SkISize{
static_cast<int32_t
>(size.
width),
94 static_cast<int32_t
>(size.
height)});
96 return SkImageInfo::MakeN32Premul(size.
width, size.
height);
104 const std::shared_ptr<GlyphAtlas>& atlas,
105 const std::vector<FontGlyphPair>& extra_pairs,
106 std::vector<Rect>& glyph_positions,
107 const std::vector<Rect>& glyph_sizes,
109 int64_t height_adjustment,
110 const std::shared_ptr<RectanglePacker>& rect_packer) {
111 TRACE_EVENT0(
"impeller", __FUNCTION__);
112 if (!rect_packer || atlas_size.
IsEmpty()) {
116 for (
size_t i = 0; i < extra_pairs.size(); i++) {
127 location_in_atlas.
x() + 1,
128 location_in_atlas.
y() + height_adjustment + 1,
134 return extra_pairs.size();
138 const std::vector<FontGlyphPair>& pairs,
139 const ISize& atlas_size,
140 std::vector<Rect>& glyph_positions,
141 const std::vector<Rect>& glyph_sizes,
142 int64_t height_adjustment,
143 const std::shared_ptr<RectanglePacker>& rect_packer,
144 size_t start_index) {
145 FML_DCHECK(!atlas_size.
IsEmpty());
147 for (
size_t i = start_index; i < pairs.size(); i++) {
157 location_in_atlas.
x() + 1,
158 location_in_atlas.
y() + height_adjustment + 1,
168 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
169 const std::vector<FontGlyphPair>& extra_pairs,
170 std::vector<Rect>& glyph_positions,
171 const std::vector<Rect>& glyph_sizes,
172 size_t glyph_index_start,
173 int64_t max_texture_height) {
176 static constexpr int64_t kAtlasWidth = 4096;
177 static constexpr int64_t kMinAtlasHeight = 1024;
179 ISize current_size =
ISize(kAtlasWidth, kMinAtlasHeight);
180 if (atlas_context->GetAtlasSize().height > current_size.
height) {
181 current_size.
height = atlas_context->GetAtlasSize().height * 2;
184 auto height_adjustment = atlas_context->GetAtlasSize().height;
185 while (current_size.
height <= max_texture_height) {
186 std::shared_ptr<RectanglePacker> rect_packer;
187 if (atlas_context->GetRectPacker() || glyph_index_start) {
190 current_size.
height - atlas_context->GetAtlasSize().height);
194 glyph_positions.erase(glyph_positions.begin() + glyph_index_start,
195 glyph_positions.end());
196 atlas_context->UpdateRectPacker(rect_packer);
198 extra_pairs, current_size, glyph_positions, glyph_sizes,
199 height_adjustment, rect_packer, glyph_index_start);
200 if (next_index == extra_pairs.size()) {
209 const SkPoint position,
212 const Rect& scaled_bounds,
213 const std::optional<GlyphProperties>& prop,
220 metrics.point_size, metrics.scaleX, metrics.skewX);
221 sk_font.setEdging(SkFont::Edging::kAntiAlias);
222 sk_font.setHinting(SkFontHinting::kSlight);
223 sk_font.setEmbolden(metrics.embolden);
224 sk_font.setSubpixel(
true);
225 sk_font.setSize(sk_font.getSize() * scaled_font.
scale);
227 auto glyph_color = prop.has_value() ? prop->color.ToARGB() : SK_ColorBLACK;
230 glyph_paint.setColor(glyph_color);
231 glyph_paint.setBlendMode(SkBlendMode::kSrc);
232 if (prop.has_value() && prop->stroke) {
233 glyph_paint.setStroke(
true);
234 glyph_paint.setStrokeWidth(prop->stroke_width * scaled_font.
scale);
235 glyph_paint.setStrokeCap(ToSkiaCap(prop->stroke_cap));
236 glyph_paint.setStrokeJoin(ToSkiaJoin(prop->stroke_join));
237 glyph_paint.setStrokeMiter(prop->stroke_miter);
241 canvas->drawGlyphs(1u,
244 SkPoint::Make(-scaled_bounds.
GetLeft(),
256 std::shared_ptr<BlitPass>& blit_pass,
258 const std::shared_ptr<Texture>& texture,
259 const std::vector<FontGlyphPair>& new_pairs,
262 TRACE_EVENT0(
"impeller", __FUNCTION__);
268 if (!bitmap.tryAllocPixels()) {
272 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
276 auto canvas = surface->getCanvas();
281 for (
size_t i = start_index; i < end_index; i++) {
284 if (!
data.has_value()) {
287 auto [pos, bounds, placeholder] =
data.value();
288 FML_DCHECK(!placeholder);
289 Size size = pos.GetSize();
294 DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
302 bitmap.getAddr(0, 0),
303 texture->GetSize().Area() *
305 atlas.
GetTexture()->GetTextureDescriptor().format),
311 texture->GetSize().height));
315 std::shared_ptr<BlitPass>& blit_pass,
317 const std::shared_ptr<Texture>& texture,
318 const std::vector<FontGlyphPair>& new_pairs,
321 TRACE_EVENT0(
"impeller", __FUNCTION__);
325 for (
size_t i = start_index; i < end_index; i++) {
328 if (!
data.has_value()) {
331 auto [pos, bounds, placeholder] =
data.value();
332 FML_DCHECK(!placeholder);
334 Size size = pos.GetSize();
345 if (!bitmap.tryAllocPixels()) {
349 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
353 auto canvas = surface->getCanvas();
364 bitmap.getAddr(0, 0),
366 atlas.
GetTexture()->GetTextureDescriptor().format),
384 return blit_pass->ConvertTextureToShaderRead(texture);
390 SkRect scaled_bounds;
393 glyph_paint.setStroke(
true);
395 glyph_paint.setStrokeCap(ToSkiaCap(glyph.
properties->stroke_cap));
396 glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.
properties->stroke_join));
397 glyph_paint.setStrokeMiter(glyph.
properties->stroke_miter);
399 font.getBounds(&glyph.
glyph.
index, 1, &scaled_bounds, &glyph_paint);
406 return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
407 scaled_bounds.fRight + adjustment,
408 scaled_bounds.fBottom);
411 std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
412 TypographerContextSkia::CollectNewGlyphs(
413 const std::shared_ptr<GlyphAtlas>& atlas,
414 const std::vector<std::shared_ptr<TextFrame>>& text_frames) {
415 std::vector<FontGlyphPair> new_glyphs;
416 std::vector<Rect> glyph_sizes;
417 size_t generation_id = atlas->GetAtlasGeneration();
418 intptr_t atlas_id =
reinterpret_cast<intptr_t
>(atlas.get());
419 for (
const auto& frame : text_frames) {
423 auto [frame_generation_id, frame_atlas_id] =
424 frame->GetAtlasGenerationAndID();
425 if (atlas->IsValid() && frame->IsFrameComplete() &&
426 frame_generation_id == generation_id && frame_atlas_id == atlas_id &&
427 !frame->GetFrameBounds(0).is_placeholder) {
431 frame->ClearFrameBounds();
432 frame->SetAtlasGeneration(generation_id, atlas_id);
434 for (
const auto& run : frame->GetRuns()) {
435 auto metrics = run.GetFont().GetMetrics();
438 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
440 FontGlyphAtlas* font_glyph_atlas =
441 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
442 FML_DCHECK(!!font_glyph_atlas);
446 metrics.point_size, metrics.scaleX, metrics.skewX);
447 sk_font.setEdging(SkFont::Edging::kAntiAlias);
448 sk_font.setHinting(SkFontHinting::kSlight);
449 sk_font.setEmbolden(metrics.embolden);
453 sk_font.setSize(sk_font.getSize() * scaled_font.scale);
454 sk_font.setSubpixel(
true);
456 for (
const auto& glyph_position : run.GetGlyphPositions()) {
458 glyph_position, scaled_font.font.GetAxisAlignment(),
459 frame->GetOffsetTransform());
460 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
461 frame->GetProperties());
462 const auto& font_glyph_bounds =
463 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
465 if (!font_glyph_bounds.has_value()) {
466 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
469 glyph_sizes.push_back(glyph_bounds);
471 auto frame_bounds = FrameBounds{
477 frame->AppendFrameBounds(frame_bounds);
478 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
480 frame->AppendFrameBounds(font_glyph_bounds.value());
485 return {std::move(new_glyphs), std::move(glyph_sizes)};
492 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
493 const std::vector<std::shared_ptr<TextFrame>>& text_frames)
const {
494 TRACE_EVENT0(
"impeller", __FUNCTION__);
498 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
499 FML_DCHECK(last_atlas->GetType() ==
type);
501 if (text_frames.empty()) {
510 auto [new_glyphs, glyph_sizes] = CollectNewGlyphs(last_atlas, text_frames);
511 if (new_glyphs.size() == 0) {
519 std::vector<Rect> glyph_positions;
520 glyph_positions.reserve(new_glyphs.size());
521 size_t first_missing_index = 0;
523 if (last_atlas->GetTexture()) {
526 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
527 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
528 atlas_context->GetRectPacker());
534 for (
size_t i = 0; i < first_missing_index; i++) {
535 last_atlas->AddTypefaceGlyphPositionAndBounds(
536 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
540 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
542 fml::ScopedCleanupClosure closure([&]() {
543 blit_pass->EncodeCommands();
545 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
554 last_atlas->GetTexture(), new_glyphs, 0,
555 first_missing_index)) {
560 if (first_missing_index == new_glyphs.size()) {
565 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
566 const int64_t max_texture_height =
574 bool blit_old_atlas =
true;
575 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
576 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
578 blit_old_atlas =
false;
579 new_atlas = std::make_shared<GlyphAtlas>(
580 type, last_atlas->GetAtlasGeneration() + 1);
582 auto [update_glyphs, update_sizes] =
583 CollectNewGlyphs(new_atlas, text_frames);
584 new_glyphs = std::move(update_glyphs);
585 glyph_sizes = std::move(update_sizes);
587 glyph_positions.clear();
588 glyph_positions.reserve(new_glyphs.size());
589 first_missing_index = 0;
591 height_adjustment = 0;
592 atlas_context->UpdateRectPacker(
nullptr);
593 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
605 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
609 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
621 descriptor.
size = atlas_size;
624 std::shared_ptr<Texture> new_texture =
630 new_texture->SetLabel(
"GlyphAtlas");
633 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
635 fml::ScopedCleanupClosure closure([&]() {
636 blit_pass->EncodeCommands();
638 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
643 auto old_texture = new_atlas->GetTexture();
644 new_atlas->SetTexture(std::move(new_texture));
650 for (
size_t i = first_missing_index; i < glyph_positions.size(); i++) {
651 new_atlas->AddTypefaceGlyphPositionAndBounds(
652 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
660 new_atlas->GetTexture(), new_glyphs,
661 first_missing_index, new_glyphs.size())) {
666 if (blit_old_atlas && old_texture) {
667 blit_pass->AddCopy(old_texture, new_atlas->GetTexture(),
static TypefaceSkia & Cast(Typeface &base)
To do anything rendering related with Impeller, you need a context.
virtual std::shared_ptr< CommandBuffer > CreateCommandBuffer() const =0
Create a new command buffer. Command buffers can be used to encode graphics, blit,...
virtual const std::shared_ptr< const Capabilities > & GetCapabilities() const =0
Get the capabilities of Impeller context. All optionally supported feature of the platform,...
virtual BackendType GetBackendType() const =0
Get the graphics backend of an Impeller context.
virtual std::shared_ptr< Allocator > GetResourceAllocator() const =0
Returns the allocator used to create textures and buffers on the device.
virtual bool EnqueueCommandBuffer(std::shared_ptr< CommandBuffer > command_buffer)
Enqueue command_buffer for submission by the end of the frame.
const std::shared_ptr< Typeface > & GetTypeface() const
The typeface whose intrinsic properties this font modifies.
const Metrics & GetMetrics() const
A texture containing the bitmap representation of glyphs in different fonts along with the ability to...
std::optional< FrameBounds > FindFontGlyphBounds(const FontGlyphPair &pair) const
Find the location of a specific font-glyph pair in the atlas.
Type
Describes how the glyphs are represented in the texture.
Type GetType() const
Describes how the glyphs are represented in the texture.
const std::shared_ptr< Texture > & GetTexture() const
Get the texture for the glyph atlas.
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
static std::shared_ptr< RectanglePacker > Factory(int width, int height)
Return an empty packer with area specified by width and height.
static Point ComputeSubpixelPosition(const TextRun::GlyphPosition &glyph_position, AxisAlignment alignment, const Matrix &transform)
static Scalar RoundScaledFontSize(Scalar scale)
const sk_sp< SkTypeface > & GetSkiaTypeface() const
virtual bool IsValid() const
~TypographerContextSkia() override
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext(GlyphAtlas::Type type) const override
static std::shared_ptr< TypographerContext > Make()
std::shared_ptr< GlyphAtlas > CreateGlyphAtlas(Context &context, GlyphAtlas::Type type, HostBuffer &host_buffer, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::vector< std::shared_ptr< TextFrame >> &text_frames) const override
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
static Rect ComputeGlyphSize(const SkFont &font, const SubpixelGlyph &glyph, Scalar scale)
static size_t PairsFitInAtlasOfSize(const std::vector< FontGlyphPair > &pairs, const ISize &atlas_size, std::vector< Rect > &glyph_positions, const std::vector< Rect > &glyph_sizes, int64_t height_adjustment, const std::shared_ptr< RectanglePacker > &rect_packer, size_t start_index)
static void DrawGlyph(SkCanvas *canvas, const SkPoint position, const ScaledFont &scaled_font, const SubpixelGlyph &glyph, const Rect &scaled_bounds, const std::optional< GlyphProperties > &prop, bool has_color)
static SkImageInfo GetImageInfo(const GlyphAtlas &atlas, Size size)
static bool BulkUpdateAtlasBitmap(const GlyphAtlas &atlas, std::shared_ptr< BlitPass > &blit_pass, HostBuffer &host_buffer, const std::shared_ptr< Texture > &texture, const std::vector< FontGlyphPair > &new_pairs, size_t start_index, size_t end_index)
Batch render to a single surface.
static size_t AppendToExistingAtlas(const std::shared_ptr< GlyphAtlas > &atlas, const std::vector< FontGlyphPair > &extra_pairs, std::vector< Rect > &glyph_positions, const std::vector< Rect > &glyph_sizes, ISize atlas_size, int64_t height_adjustment, const std::shared_ptr< RectanglePacker > &rect_packer)
static ISize ComputeNextAtlasSize(const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::vector< FontGlyphPair > &extra_pairs, std::vector< Rect > &glyph_positions, const std::vector< Rect > &glyph_sizes, size_t glyph_index_start, int64_t max_texture_height)
static bool UpdateAtlasBitmap(const GlyphAtlas &atlas, std::shared_ptr< BlitPass > &blit_pass, HostBuffer &host_buffer, const std::shared_ptr< Texture > &texture, const std::vector< FontGlyphPair > &new_pairs, size_t start_index, size_t end_index)
constexpr size_t DefaultUniformAlignment()
A font along with a glyph in that font rendered at a particular scale and subpixel position.
A font and a scale. Used as a key that represents a typeface within a glyph atlas.
A glyph and its subpixel position.
std::optional< GlyphProperties > properties
constexpr auto GetTop() const
constexpr auto GetLeft() const
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
constexpr static TRect MakeSize(const TSize< U > &size)
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
constexpr Type Area() const
constexpr TSize Ceil() const
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
std::shared_ptr< const fml::Mapping > data