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"
51SkPaint::Cap ToSkiaCap(
Cap cap) {
54 return SkPaint::Cap::kButt_Cap;
56 return SkPaint::Cap::kRound_Cap;
58 return SkPaint::Cap::kSquare_Cap;
63SkPaint::Join ToSkiaJoin(
Join join) {
66 return SkPaint::Join::kMiter_Join;
68 return SkPaint::Join::kRound_Join;
70 return SkPaint::Join::kBevel_Join;
77 return std::make_shared<TypographerContextSkia>();
84std::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) {
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) {
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,
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();
299 Size size = pos.GetSize();
300 if (size.IsEmpty()) {
304 DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
312 bitmap.getAddr(0, 0),
315 atlas.
GetTexture()->GetTextureDescriptor().format),
325 std::shared_ptr<BlitPass>& blit_pass,
327 const std::shared_ptr<Texture>&
texture,
328 const std::vector<FontGlyphPair>& new_pairs,
335 for (
size_t i = start_index;
i < end_index;
i++) {
338 if (!
data.has_value()) {
341 auto [pos, bounds, placeholder] =
data.value();
344 Size size = pos.GetSize();
345 if (size.IsEmpty()) {
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),
385 size.width, size.height),
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);
422std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
423TypographerContextSkia::CollectNewGlyphs(
424 const std::shared_ptr<GlyphAtlas>& atlas,
425 const std::vector<std::shared_ptr<TextFrame>>& text_frames) {
426 std::vector<FontGlyphPair> new_glyphs;
427 std::vector<Rect> glyph_sizes;
428 size_t generation_id = atlas->GetAtlasGeneration();
429 intptr_t atlas_id =
reinterpret_cast<intptr_t
>(atlas.get());
430 for (
const auto& frame : text_frames) {
437 auto [frame_generation_id, frame_atlas_id] =
438 frame->GetAtlasGenerationAndID();
439 if (atlas->IsValid() && frame->IsFrameComplete() &&
440 frame_generation_id == generation_id && frame_atlas_id == atlas_id &&
441 !frame->GetFrameBounds(0).is_placeholder) {
445 frame->ClearFrameBounds();
446 frame->SetAtlasGeneration(generation_id, atlas_id);
448 for (
const auto& run : frame->GetRuns()) {
449 auto metrics = run.GetFont().GetMetrics();
452 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
454 FontGlyphAtlas* font_glyph_atlas =
455 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
460 metrics.point_size, metrics.scaleX, metrics.skewX);
461 sk_font.setEdging(SkFont::Edging::kAntiAlias);
462 sk_font.setHinting(SkFontHinting::kSlight);
463 sk_font.setEmbolden(metrics.embolden);
467 sk_font.setSize(sk_font.getSize() *
468 static_cast<Scalar>(scaled_font.scale));
469 sk_font.setSubpixel(
true);
471 for (
const auto& glyph_position : run.GetGlyphPositions()) {
473 glyph_position, scaled_font.font.GetAxisAlignment(),
474 frame->GetOffsetTransform());
475 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
476 frame->GetProperties());
477 const auto& font_glyph_bounds =
478 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
480 if (!font_glyph_bounds.has_value()) {
481 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
483 sk_font, subpixel_glyph,
static_cast<Scalar>(scaled_font.scale));
484 glyph_sizes.push_back(glyph_bounds);
486 auto frame_bounds = FrameBounds{
492 frame->AppendFrameBounds(frame_bounds);
493 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
495 frame->AppendFrameBounds(font_glyph_bounds.value());
500 return {std::move(new_glyphs), std::move(glyph_sizes)};
507 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
508 const std::vector<std::shared_ptr<TextFrame>>& text_frames)
const {
513 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
516 if (text_frames.empty()) {
525 auto [new_glyphs, glyph_sizes] = CollectNewGlyphs(last_atlas, text_frames);
526 if (new_glyphs.size() == 0) {
534 std::vector<Rect> glyph_positions;
535 glyph_positions.reserve(new_glyphs.size());
536 size_t first_missing_index = 0;
538 if (last_atlas->GetTexture()) {
541 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
542 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
543 atlas_context->GetRectPacker());
549 for (
size_t i = 0;
i < first_missing_index;
i++) {
550 last_atlas->AddTypefaceGlyphPositionAndBounds(
551 new_glyphs[
i], glyph_positions[
i], glyph_sizes[
i]);
555 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
558 blit_pass->EncodeCommands();
560 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
569 last_atlas->GetTexture(), new_glyphs, 0,
570 first_missing_index)) {
575 if (first_missing_index == new_glyphs.size()) {
580 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
581 const int64_t max_texture_height =
589 bool blit_old_atlas =
true;
590 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
591 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
593 blit_old_atlas =
false;
594 new_atlas = std::make_shared<GlyphAtlas>(
595 type, last_atlas->GetAtlasGeneration() + 1);
597 auto [update_glyphs, update_sizes] =
598 CollectNewGlyphs(new_atlas, text_frames);
599 new_glyphs = std::move(update_glyphs);
600 glyph_sizes = std::move(update_sizes);
602 glyph_positions.clear();
603 glyph_positions.reserve(new_glyphs.size());
604 first_missing_index = 0;
606 height_adjustment = 0;
607 atlas_context->UpdateRectPacker(
nullptr);
608 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
620 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
624 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
636 descriptor.
size = atlas_size;
639 std::shared_ptr<Texture> new_texture =
645 new_texture->SetLabel(
"GlyphAtlas");
648 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
651 blit_pass->EncodeCommands();
653 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
658 auto old_texture = new_atlas->GetTexture();
659 new_atlas->SetTexture(std::move(new_texture));
665 for (
size_t i = first_missing_index;
i < glyph_positions.size();
i++) {
666 new_atlas->AddTypefaceGlyphPositionAndBounds(
667 new_glyphs[
i], glyph_positions[
i], glyph_sizes[
i]);
675 new_atlas->GetTexture(), new_glyphs,
676 first_missing_index, new_glyphs.size())) {
681 if (blit_old_atlas && old_texture) {
682 blit_pass->AddCopy(old_texture, new_atlas->GetTexture(),
Wraps a closure that is invoked in the destructor unless released by the caller.
static TypefaceSkia & Cast(Typeface &base)
To do anything rendering related with Impeller, you need a context.
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< CommandBuffer > CreateCommandBuffer() const =0
Create a new command buffer. Command buffers can be used to encode graphics, blit,...
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
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
~TypographerContextSkia() override
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext(GlyphAtlas::Type type) const override
static std::shared_ptr< TypographerContext > Make()
#define FML_UNREACHABLE()
#define FML_DCHECK(condition)
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
static constexpr TRect MakeXYWH(Type x, Type y, Type width, Type height)
static constexpr TRect MakeSize(const TSize< U > &size)
static constexpr TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
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
#define TRACE_EVENT0(category_group, name)