14 #include "flutter/fml/logging.h"
15 #include "flutter/fml/trace_event.h"
16 #include "fml/closure.h"
36 #include "third_party/skia/include/core/SkBitmap.h"
37 #include "third_party/skia/include/core/SkBlendMode.h"
38 #include "third_party/skia/include/core/SkCanvas.h"
39 #include "third_party/skia/include/core/SkColor.h"
40 #include "third_party/skia/include/core/SkFont.h"
41 #include "third_party/skia/include/core/SkImageInfo.h"
42 #include "third_party/skia/include/core/SkPaint.h"
43 #include "third_party/skia/include/core/SkSize.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);
252 canvas->drawGlyphs({&glyph_id, 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),
318 return blit_pass->AddCopy(std::move(buffer_view),
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),
382 if (!blit_pass->AddCopy(std::move(buffer_view),
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);
410 font.getBounds({&glyph.
glyph.
index, 1}, {&scaled_bounds, 1}, &glyph_paint);
417 return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
418 scaled_bounds.fRight + adjustment,
419 scaled_bounds.fBottom);
422 std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
423 TypographerContextSkia::CollectNewGlyphs(
424 const std::shared_ptr<GlyphAtlas>& atlas,
425 const std::vector<RenderableText>& renderable_texts) {
426 std::vector<FontGlyphPair> new_glyphs;
427 std::vector<Rect> glyph_sizes;
428 for (
const auto& frame : renderable_texts) {
430 frame.origin_transform.GetMaxBasisLengthXY());
431 for (
const auto& run : frame.text_frame->GetRuns()) {
432 auto metrics = run.GetFont().GetMetrics();
434 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
436 FontGlyphAtlas* font_glyph_atlas =
437 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
438 FML_DCHECK(!!font_glyph_atlas);
442 metrics.point_size, metrics.scaleX, metrics.skewX);
443 sk_font.setEdging(SkFont::Edging::kAntiAlias);
444 sk_font.setHinting(SkFontHinting::kSlight);
445 sk_font.setEmbolden(metrics.embolden);
449 sk_font.setSize(sk_font.getSize() *
450 static_cast<Scalar>(scaled_font.scale));
451 sk_font.setSubpixel(
true);
453 for (
const auto& glyph_position : run.GetGlyphPositions()) {
455 glyph_position, scaled_font.font.GetAxisAlignment(),
456 frame.origin_transform);
457 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
459 const auto& font_glyph_bounds =
460 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
462 if (!font_glyph_bounds.has_value()) {
463 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
465 sk_font, subpixel_glyph,
static_cast<Scalar>(scaled_font.scale));
466 glyph_sizes.push_back(glyph_bounds);
468 auto frame_bounds = FrameBounds{
474 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
479 return {std::move(new_glyphs), std::move(glyph_sizes)};
486 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
487 const std::vector<RenderableText>& renderable_texts)
const {
488 TRACE_EVENT0(
"impeller", __FUNCTION__);
492 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
493 FML_DCHECK(last_atlas->GetType() == type);
495 if (renderable_texts.empty()) {
504 auto [new_glyphs, glyph_sizes] =
505 CollectNewGlyphs(last_atlas, renderable_texts);
506 if (new_glyphs.size() == 0) {
514 std::vector<Rect> glyph_positions;
515 glyph_positions.reserve(new_glyphs.size());
516 size_t first_missing_index = 0;
518 if (last_atlas->GetTexture()) {
521 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
522 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
523 atlas_context->GetRectPacker());
529 for (
size_t i = 0; i < first_missing_index; i++) {
530 last_atlas->AddTypefaceGlyphPositionAndBounds(
531 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
535 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
537 fml::ScopedCleanupClosure closure([&]() {
538 blit_pass->EncodeCommands();
540 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
549 last_atlas->GetTexture(), new_glyphs, 0,
550 first_missing_index)) {
555 if (first_missing_index == new_glyphs.size()) {
560 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
561 const int64_t max_texture_height =
569 bool blit_old_atlas =
true;
570 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
571 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
573 blit_old_atlas =
false;
574 new_atlas = std::make_shared<GlyphAtlas>(
575 type, last_atlas->GetAtlasGeneration() + 1);
577 auto [update_glyphs, update_sizes] =
578 CollectNewGlyphs(new_atlas, renderable_texts);
579 new_glyphs = std::move(update_glyphs);
580 glyph_sizes = std::move(update_sizes);
582 glyph_positions.clear();
583 glyph_positions.reserve(new_glyphs.size());
584 first_missing_index = 0;
586 height_adjustment = 0;
587 atlas_context->UpdateRectPacker(
nullptr);
588 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
600 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
604 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
616 descriptor.
size = atlas_size;
619 std::shared_ptr<Texture> new_texture =
625 new_texture->SetLabel(
"GlyphAtlas");
628 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
630 fml::ScopedCleanupClosure closure([&]() {
631 blit_pass->EncodeCommands();
633 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
638 auto old_texture = new_atlas->GetTexture();
639 new_atlas->SetTexture(std::move(new_texture));
645 for (
size_t i = first_missing_index; i < glyph_positions.size(); i++) {
646 new_atlas->AddTypefaceGlyphPositionAndBounds(
647 new_glyphs[i], glyph_positions[i], glyph_sizes[i]);
655 new_atlas->GetTexture(), new_glyphs,
656 first_missing_index, new_glyphs.size())) {
661 if (blit_old_atlas && old_texture) {
662 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< GlyphAtlas > CreateGlyphAtlas(Context &context, GlyphAtlas::Type type, HostBuffer &host_buffer, const std::shared_ptr< GlyphAtlasContext > &atlas_context, const std::vector< RenderableText > &renderable_texts) const override
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext(GlyphAtlas::Type type) const override
static std::shared_ptr< TypographerContext > Make()
constexpr size_t BytesPerPixelForPixelFormat(PixelFormat format)
Join
An enum that describes ways to join two segments of a path.
static bool UpdateAtlasBitmap(const GlyphAtlas &atlas, std::shared_ptr< BlitPass > &blit_pass, HostBuffer &data_host_buffer, const std::shared_ptr< Texture > &texture, const std::vector< FontGlyphPair > &new_pairs, size_t start_index, size_t end_index)
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)
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 BulkUpdateAtlasBitmap(const GlyphAtlas &atlas, std::shared_ptr< BlitPass > &blit_pass, HostBuffer &data_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.
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...