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 return Point((pos & 0xff) / 4.f, (pos >> 2 & 0xff) / 4.f);
213 const SkPoint position,
216 const Rect& scaled_bounds,
217 const std::optional<GlyphProperties>& prop,
224 metrics.point_size, metrics.scaleX, metrics.skewX);
225 sk_font.setEdging(SkFont::Edging::kAntiAlias);
226 sk_font.setHinting(SkFontHinting::kSlight);
227 sk_font.setEmbolden(metrics.embolden);
228 sk_font.setSubpixel(
true);
229 sk_font.setSize(sk_font.getSize() *
static_cast<Scalar>(scaled_font.
scale));
231 auto glyph_color = prop.has_value() ? prop->color.ToARGB() : SK_ColorBLACK;
234 glyph_paint.setColor(glyph_color);
235 glyph_paint.setBlendMode(SkBlendMode::kSrc);
236 if (prop.has_value() && prop->stroke) {
237 glyph_paint.setStroke(
true);
238 glyph_paint.setStrokeWidth(prop->stroke_width *
240 glyph_paint.setStrokeCap(ToSkiaCap(prop->stroke_cap));
241 glyph_paint.setStrokeJoin(ToSkiaJoin(prop->stroke_join));
242 glyph_paint.setStrokeMiter(prop->stroke_miter);
246 canvas->translate(subpixel_offset.
x, subpixel_offset.
y);
247 canvas->drawGlyphs(1u,
250 SkPoint::Make(-scaled_bounds.
GetLeft(),
262 std::shared_ptr<BlitPass>& blit_pass,
264 const std::shared_ptr<Texture>& texture,
265 const std::vector<FontGlyphPair>& new_pairs,
268 TRACE_EVENT0(
"impeller", __FUNCTION__);
274 if (!bitmap.tryAllocPixels()) {
278 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
282 auto canvas = surface->getCanvas();
287 for (
size_t i = start_index; i < end_index; i++) {
290 if (!
data.has_value()) {
293 auto [pos, bounds, placeholder] =
data.value();
294 FML_DCHECK(!placeholder);
295 Size size = pos.GetSize();
300 DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
308 bitmap.getAddr(0, 0),
309 texture->GetSize().Area() *
311 atlas.
GetTexture()->GetTextureDescriptor().format),
317 texture->GetSize().height));
321 std::shared_ptr<BlitPass>& blit_pass,
323 const std::shared_ptr<Texture>& texture,
324 const std::vector<FontGlyphPair>& new_pairs,
327 TRACE_EVENT0(
"impeller", __FUNCTION__);
331 for (
size_t i = start_index; i < end_index; i++) {
334 if (!
data.has_value()) {
337 auto [pos, bounds, placeholder] =
data.value();
338 FML_DCHECK(!placeholder);
340 Size size = pos.GetSize();
351 if (!bitmap.tryAllocPixels()) {
355 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
359 auto canvas = surface->getCanvas();
370 bitmap.getAddr(0, 0),
372 atlas.
GetTexture()->GetTextureDescriptor().format),
390 return blit_pass->ConvertTextureToShaderRead(texture);
396 SkRect scaled_bounds;
399 glyph_paint.setStroke(
true);
401 glyph_paint.setStrokeCap(ToSkiaCap(glyph.
properties->stroke_cap));
402 glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.
properties->stroke_join));
403 glyph_paint.setStrokeMiter(glyph.
properties->stroke_miter);
405 font.getBounds(&glyph.
glyph.
index, 1, &scaled_bounds, &glyph_paint);
412 return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
413 scaled_bounds.fRight + adjustment,
414 scaled_bounds.fBottom);
417 std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
418 TypographerContextSkia::CollectNewGlyphs(
419 const std::shared_ptr<GlyphAtlas>& atlas,
420 const std::vector<std::shared_ptr<TextFrame>>& text_frames) {
421 std::vector<FontGlyphPair> new_glyphs;
422 std::vector<Rect> glyph_sizes;
423 size_t generation_id = atlas->GetAtlasGeneration();
424 intptr_t atlas_id =
reinterpret_cast<intptr_t
>(atlas.get());
425 for (
const auto& frame : text_frames) {
432 auto [frame_generation_id, frame_atlas_id] =
433 frame->GetAtlasGenerationAndID();
434 if (atlas->IsValid() && frame->IsFrameComplete() &&
435 frame_generation_id == generation_id && frame_atlas_id == atlas_id &&
436 !frame->GetFrameBounds(0).is_placeholder) {
440 frame->ClearFrameBounds();
441 frame->SetAtlasGeneration(generation_id, atlas_id);
443 for (
const auto& run : frame->GetRuns()) {
444 auto metrics = run.GetFont().GetMetrics();
447 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
449 FontGlyphAtlas* font_glyph_atlas =
450 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
451 FML_DCHECK(!!font_glyph_atlas);
455 metrics.point_size, metrics.scaleX, metrics.skewX);
456 sk_font.setEdging(SkFont::Edging::kAntiAlias);
457 sk_font.setHinting(SkFontHinting::kSlight);
458 sk_font.setEmbolden(metrics.embolden);
462 sk_font.setSize(sk_font.getSize() *
463 static_cast<Scalar>(scaled_font.scale));
464 sk_font.setSubpixel(
true);
466 for (
const auto& glyph_position : run.GetGlyphPositions()) {
468 glyph_position, scaled_font.font.GetAxisAlignment(),
469 frame->GetOffsetTransform());
470 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
471 frame->GetProperties());
472 const auto& font_glyph_bounds =
473 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
475 if (!font_glyph_bounds.has_value()) {
476 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
478 sk_font, subpixel_glyph,
static_cast<Scalar>(scaled_font.scale));
479 glyph_sizes.push_back(glyph_bounds);
481 auto frame_bounds = FrameBounds{
487 frame->AppendFrameBounds(frame_bounds);
488 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
490 frame->AppendFrameBounds(font_glyph_bounds.value());
495 return {std::move(new_glyphs), std::move(glyph_sizes)};
502 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
503 const std::vector<std::shared_ptr<TextFrame>>& text_frames)
const {
504 TRACE_EVENT0(
"impeller", __FUNCTION__);
508 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
509 FML_DCHECK(last_atlas->GetType() ==
type);
511 if (text_frames.empty()) {
520 auto [new_glyphs, glyph_sizes] = CollectNewGlyphs(last_atlas, text_frames);
521 if (new_glyphs.size() == 0) {
529 std::vector<Rect> glyph_positions;
530 glyph_positions.reserve(new_glyphs.size());
531 size_t first_missing_index = 0;
533 if (last_atlas->GetTexture()) {
536 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
537 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
538 atlas_context->GetRectPacker());
544 for (
size_t i = 0; i < first_missing_index; i++) {
545 last_atlas->AddTypefaceGlyphPositionAndBounds(
546 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
550 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
552 fml::ScopedCleanupClosure closure([&]() {
553 blit_pass->EncodeCommands();
555 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
564 last_atlas->GetTexture(), new_glyphs, 0,
565 first_missing_index)) {
570 if (first_missing_index == new_glyphs.size()) {
575 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
576 const int64_t max_texture_height =
584 bool blit_old_atlas =
true;
585 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
586 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
588 blit_old_atlas =
false;
589 new_atlas = std::make_shared<GlyphAtlas>(
590 type, last_atlas->GetAtlasGeneration() + 1);
592 auto [update_glyphs, update_sizes] =
593 CollectNewGlyphs(new_atlas, text_frames);
594 new_glyphs = std::move(update_glyphs);
595 glyph_sizes = std::move(update_sizes);
597 glyph_positions.clear();
598 glyph_positions.reserve(new_glyphs.size());
599 first_missing_index = 0;
601 height_adjustment = 0;
602 atlas_context->UpdateRectPacker(
nullptr);
603 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
615 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
619 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
631 descriptor.
size = atlas_size;
634 std::shared_ptr<Texture> new_texture =
640 new_texture->SetLabel(
"GlyphAtlas");
643 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
645 fml::ScopedCleanupClosure closure([&]() {
646 blit_pass->EncodeCommands();
648 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
653 auto old_texture = new_atlas->GetTexture();
654 new_atlas->SetTexture(std::move(new_texture));
660 for (
size_t i = first_missing_index; i < glyph_positions.size(); i++) {
661 new_atlas->AddTypefaceGlyphPositionAndBounds(
662 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
670 new_atlas->GetTexture(), new_glyphs,
671 first_missing_index, new_glyphs.size())) {
676 if (blit_old_atlas && old_texture) {
677 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 Rational RoundScaledFontSize(Scalar scale)
static SubpixelPosition ComputeSubpixelPosition(const TextRun::GlyphPosition &glyph_position, AxisAlignment alignment, const Matrix &transform)
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 Point SubpixelPositionToPoint(SubpixelPosition pos)
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
SubpixelPosition subpixel_offset
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