14 #include "flutter/fml/logging.h"
15 #include "flutter/fml/trace_event.h"
16 #include "fml/closure.h"
35 #include "include/core/SkColor.h"
36 #include "include/core/SkImageInfo.h"
37 #include "include/core/SkPaint.h"
38 #include "include/core/SkSize.h"
40 #include "third_party/skia/include/core/SkBitmap.h"
41 #include "third_party/skia/include/core/SkBlendMode.h"
42 #include "third_party/skia/include/core/SkCanvas.h"
43 #include "third_party/skia/include/core/SkFont.h"
44 #include "third_party/skia/include/core/SkSurface.h"
54 return SkPaint::Cap::kButt_Cap;
56 return SkPaint::Cap::kRound_Cap;
58 return SkPaint::Cap::kSquare_Cap;
66 return SkPaint::Join::kMiter_Join;
68 return SkPaint::Join::kRound_Join;
70 return SkPaint::Join::kBevel_Join;
77 return std::make_shared<TypographerContextSkia>();
84 std::shared_ptr<GlyphAtlasContext>
86 return std::make_shared<GlyphAtlasContext>(
type);
92 return SkImageInfo::MakeA8(SkISize{
static_cast<int32_t
>(size.
width),
93 static_cast<int32_t
>(size.
height)});
95 return SkImageInfo::MakeN32Premul(size.
width, size.
height);
103 const std::shared_ptr<GlyphAtlas>& atlas,
104 const std::vector<FontGlyphPair>& extra_pairs,
105 std::vector<Rect>& glyph_positions,
106 const std::vector<Rect>& glyph_sizes,
108 int64_t height_adjustment,
109 const std::shared_ptr<RectanglePacker>& rect_packer) {
110 TRACE_EVENT0(
"impeller", __FUNCTION__);
111 if (!rect_packer || atlas_size.
IsEmpty()) {
115 for (
size_t i = 0; i < extra_pairs.size(); i++) {
126 location_in_atlas.
x() + 1,
127 location_in_atlas.
y() + height_adjustment + 1,
133 return extra_pairs.size();
137 const std::vector<FontGlyphPair>& pairs,
138 const ISize& atlas_size,
139 std::vector<Rect>& glyph_positions,
140 const std::vector<Rect>& glyph_sizes,
141 int64_t height_adjustment,
142 const std::shared_ptr<RectanglePacker>& rect_packer,
143 size_t start_index) {
144 FML_DCHECK(!atlas_size.
IsEmpty());
146 for (
size_t i = start_index; i < pairs.size(); i++) {
156 location_in_atlas.
x() + 1,
157 location_in_atlas.
y() + height_adjustment + 1,
167 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
168 const std::vector<FontGlyphPair>& extra_pairs,
169 std::vector<Rect>& glyph_positions,
170 const std::vector<Rect>& glyph_sizes,
171 size_t glyph_index_start,
172 int64_t max_texture_height) {
175 static constexpr int64_t kAtlasWidth = 4096;
176 static constexpr int64_t kMinAtlasHeight = 1024;
178 ISize current_size =
ISize(kAtlasWidth, kMinAtlasHeight);
179 if (atlas_context->GetAtlasSize().height > current_size.
height) {
180 current_size.
height = atlas_context->GetAtlasSize().height * 2;
183 auto height_adjustment = atlas_context->GetAtlasSize().height;
184 while (current_size.
height <= max_texture_height) {
185 std::shared_ptr<RectanglePacker> rect_packer;
186 if (atlas_context->GetRectPacker() || glyph_index_start) {
189 current_size.
height - atlas_context->GetAtlasSize().height);
193 glyph_positions.erase(glyph_positions.begin() + glyph_index_start,
194 glyph_positions.end());
195 atlas_context->UpdateRectPacker(rect_packer);
197 extra_pairs, current_size, glyph_positions, glyph_sizes,
198 height_adjustment, rect_packer, glyph_index_start);
199 if (next_index == extra_pairs.size()) {
208 return Point((pos & 0xff) / 4.f, (pos >> 2 & 0xff) / 4.f);
212 const SkPoint position,
215 const Rect& scaled_bounds,
216 const std::optional<GlyphProperties>& prop,
223 metrics.point_size, metrics.scaleX, metrics.skewX);
224 sk_font.setEdging(SkFont::Edging::kAntiAlias);
225 sk_font.setHinting(SkFontHinting::kSlight);
226 sk_font.setEmbolden(metrics.embolden);
227 sk_font.setSubpixel(
true);
228 sk_font.setSize(sk_font.getSize() *
static_cast<Scalar>(scaled_font.
scale));
230 auto glyph_color = prop.has_value() ? prop->color.ToARGB() : SK_ColorBLACK;
233 glyph_paint.setColor(glyph_color);
234 glyph_paint.setBlendMode(SkBlendMode::kSrc);
235 if (prop.has_value()) {
236 auto stroke = prop->stroke;
237 if (stroke.has_value()) {
238 glyph_paint.setStroke(
true);
239 glyph_paint.setStrokeWidth(stroke->width *
241 glyph_paint.setStrokeCap(ToSkiaCap(stroke->cap));
242 glyph_paint.setStrokeJoin(ToSkiaJoin(stroke->join));
243 glyph_paint.setStrokeMiter(stroke->miter_limit);
245 glyph_paint.setStroke(
false);
250 canvas->translate(subpixel_offset.
x, subpixel_offset.
y);
251 canvas->drawGlyphs(1u,
254 SkPoint::Make(-scaled_bounds.
GetLeft(),
266 std::shared_ptr<BlitPass>& blit_pass,
268 const std::shared_ptr<Texture>& texture,
269 const std::vector<FontGlyphPair>& new_pairs,
272 TRACE_EVENT0(
"impeller", __FUNCTION__);
278 if (!bitmap.tryAllocPixels()) {
282 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
286 auto canvas = surface->getCanvas();
291 for (
size_t i = start_index; i < end_index; i++) {
294 if (!
data.has_value()) {
297 auto [pos, bounds, placeholder] =
data.value();
298 FML_DCHECK(!placeholder);
299 Size size = pos.GetSize();
304 DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
312 bitmap.getAddr(0, 0),
313 texture->GetSize().Area() *
315 atlas.
GetTexture()->GetTextureDescriptor().format),
321 texture->GetSize().height));
325 std::shared_ptr<BlitPass>& blit_pass,
327 const std::shared_ptr<Texture>& texture,
328 const std::vector<FontGlyphPair>& new_pairs,
331 TRACE_EVENT0(
"impeller", __FUNCTION__);
335 for (
size_t i = start_index; i < end_index; i++) {
338 if (!
data.has_value()) {
341 auto [pos, bounds, placeholder] =
data.value();
342 FML_DCHECK(!placeholder);
344 Size size = pos.GetSize();
355 if (!bitmap.tryAllocPixels()) {
359 auto surface = SkSurfaces::WrapPixels(bitmap.pixmap());
363 auto canvas = surface->getCanvas();
374 bitmap.getAddr(0, 0),
376 atlas.
GetTexture()->GetTextureDescriptor().format),
394 return blit_pass->ConvertTextureToShaderRead(texture);
400 SkRect scaled_bounds;
403 glyph_paint.setStroke(
true);
404 glyph_paint.setStrokeWidth(glyph.
properties->stroke->width * scale);
405 glyph_paint.setStrokeCap(ToSkiaCap(glyph.
properties->stroke->cap));
406 glyph_paint.setStrokeJoin(ToSkiaJoin(glyph.
properties->stroke->join));
407 glyph_paint.setStrokeMiter(glyph.
properties->stroke->miter_limit);
409 font.getBounds(&glyph.
glyph.
index, 1, &scaled_bounds, &glyph_paint);
416 return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
417 scaled_bounds.fRight + adjustment,
418 scaled_bounds.fBottom);
421 std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
422 TypographerContextSkia::CollectNewGlyphs(
423 const std::shared_ptr<GlyphAtlas>& atlas,
424 const std::vector<std::shared_ptr<TextFrame>>& text_frames) {
425 std::vector<FontGlyphPair> new_glyphs;
426 std::vector<Rect> glyph_sizes;
427 size_t generation_id = atlas->GetAtlasGeneration();
428 intptr_t atlas_id =
reinterpret_cast<intptr_t
>(atlas.get());
429 for (
const auto& frame : text_frames) {
436 auto [frame_generation_id, frame_atlas_id] =
437 frame->GetAtlasGenerationAndID();
438 if (atlas->IsValid() && frame->IsFrameComplete() &&
439 frame_generation_id == generation_id && frame_atlas_id == atlas_id &&
440 !frame->GetFrameBounds(0).is_placeholder) {
444 frame->ClearFrameBounds();
445 frame->SetAtlasGeneration(generation_id, atlas_id);
447 for (
const auto& run : frame->GetRuns()) {
448 auto metrics = run.GetFont().GetMetrics();
451 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
453 FontGlyphAtlas* font_glyph_atlas =
454 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
455 FML_DCHECK(!!font_glyph_atlas);
459 metrics.point_size, metrics.scaleX, metrics.skewX);
460 sk_font.setEdging(SkFont::Edging::kAntiAlias);
461 sk_font.setHinting(SkFontHinting::kSlight);
462 sk_font.setEmbolden(metrics.embolden);
466 sk_font.setSize(sk_font.getSize() *
467 static_cast<Scalar>(scaled_font.scale));
468 sk_font.setSubpixel(
true);
470 for (
const auto& glyph_position : run.GetGlyphPositions()) {
472 glyph_position, scaled_font.font.GetAxisAlignment(),
473 frame->GetOffsetTransform());
474 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
475 frame->GetProperties());
476 const auto& font_glyph_bounds =
477 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
479 if (!font_glyph_bounds.has_value()) {
480 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
482 sk_font, subpixel_glyph,
static_cast<Scalar>(scaled_font.scale));
483 glyph_sizes.push_back(glyph_bounds);
485 auto frame_bounds = FrameBounds{
491 frame->AppendFrameBounds(frame_bounds);
492 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
494 frame->AppendFrameBounds(font_glyph_bounds.value());
499 return {std::move(new_glyphs), std::move(glyph_sizes)};
506 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
507 const std::vector<std::shared_ptr<TextFrame>>& text_frames)
const {
508 TRACE_EVENT0(
"impeller", __FUNCTION__);
512 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
513 FML_DCHECK(last_atlas->GetType() ==
type);
515 if (text_frames.empty()) {
524 auto [new_glyphs, glyph_sizes] = CollectNewGlyphs(last_atlas, text_frames);
525 if (new_glyphs.size() == 0) {
533 std::vector<Rect> glyph_positions;
534 glyph_positions.reserve(new_glyphs.size());
535 size_t first_missing_index = 0;
537 if (last_atlas->GetTexture()) {
540 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
541 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
542 atlas_context->GetRectPacker());
548 for (
size_t i = 0; i < first_missing_index; i++) {
549 last_atlas->AddTypefaceGlyphPositionAndBounds(
550 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
554 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
556 fml::ScopedCleanupClosure closure([&]() {
557 blit_pass->EncodeCommands();
559 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
568 last_atlas->GetTexture(), new_glyphs, 0,
569 first_missing_index)) {
574 if (first_missing_index == new_glyphs.size()) {
579 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
580 const int64_t max_texture_height =
588 bool blit_old_atlas =
true;
589 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
590 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
592 blit_old_atlas =
false;
593 new_atlas = std::make_shared<GlyphAtlas>(
594 type, last_atlas->GetAtlasGeneration() + 1);
596 auto [update_glyphs, update_sizes] =
597 CollectNewGlyphs(new_atlas, text_frames);
598 new_glyphs = std::move(update_glyphs);
599 glyph_sizes = std::move(update_sizes);
601 glyph_positions.clear();
602 glyph_positions.reserve(new_glyphs.size());
603 first_missing_index = 0;
605 height_adjustment = 0;
606 atlas_context->UpdateRectPacker(
nullptr);
607 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
619 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
623 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
635 descriptor.
size = atlas_size;
638 std::shared_ptr<Texture> new_texture =
644 new_texture->SetLabel(
"GlyphAtlas");
647 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
649 fml::ScopedCleanupClosure closure([&]() {
650 blit_pass->EncodeCommands();
652 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
657 auto old_texture = new_atlas->GetTexture();
658 new_atlas->SetTexture(std::move(new_texture));
664 for (
size_t i = first_missing_index; i < glyph_positions.size(); i++) {
665 new_atlas->AddTypefaceGlyphPositionAndBounds(
666 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
674 new_atlas->GetTexture(), new_glyphs,
675 first_missing_index, new_glyphs.size())) {
680 if (blit_old_atlas && old_texture) {
681 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.
size_t GetMinimumUniformAlignment() const
Retrieve the minimum uniform buffer alignment in bytes.
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)
Join
An enum that describes ways to join two segments of a path.
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.
Cap
An enum that describes ways to decorate the end of a path contour.
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)
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