36#include "third_party/abseil-cpp/absl/status/statusor.h"
37#include "third_party/skia/include/core/SkBitmap.h"
38#include "third_party/skia/include/core/SkBlendMode.h"
39#include "third_party/skia/include/core/SkCanvas.h"
40#include "third_party/skia/include/core/SkColor.h"
41#include "third_party/skia/include/core/SkFont.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"
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;
76 const std::vector<FontGlyphPair>& new_pairs,
82 for (
size_t i = start_index;
i < end_index;
i++) {
83 if (new_pairs[
i].glyph.properties.tone_or_color ==
92absl::StatusOr<SkBitmap> ToA8Bitmap(
const SkBitmap& src) {
93 FML_DCHECK(src.colorType() == kRGBA_8888_SkColorType);
96 a8_bitmap.setInfo(SkImageInfo::MakeA8(src.width(), src.height()));
97 if (!a8_bitmap.tryAllocPixels()) {
98 return absl::Status(absl::StatusCode::kInternal,
99 "Failed to allocate pixels for A8 bitmap");
101 if (!src.readPixels(a8_bitmap.pixmap())) {
102 return absl::Status(absl::StatusCode::kInternal,
103 "Failed to read pixels into A8 bitmap");
111 return std::make_shared<TypographerContextSkia>();
118std::shared_ptr<GlyphAtlasContext>
120 return std::make_shared<GlyphAtlasContext>(
type);
125 bool support_light_glyphs) {
126 SkISize skia_size = {
static_cast<int32_t
>(size.width),
127 static_cast<int32_t
>(size.height)};
131 return support_light_glyphs
132 ? SkImageInfo::Make(skia_size, kRGBA_8888_SkColorType,
134 : SkImageInfo::MakeA8(skia_size);
136 return SkImageInfo::Make(skia_size, kRGBA_8888_SkColorType,
137 kPremul_SkAlphaType);
145 const std::shared_ptr<GlyphAtlas>& atlas,
146 const std::vector<FontGlyphPair>& extra_pairs,
147 std::vector<Rect>& glyph_positions,
148 const std::vector<Rect>& glyph_sizes,
150 int64_t height_adjustment,
151 const std::shared_ptr<RectanglePacker>& rect_packer) {
153 if (!rect_packer || atlas_size.
IsEmpty()) {
157 for (
size_t i = 0;
i < extra_pairs.size();
i++) {
168 location_in_atlas.
x() + 1,
169 location_in_atlas.
y() + height_adjustment + 1,
175 return extra_pairs.size();
179 const std::vector<FontGlyphPair>& pairs,
180 const ISize& atlas_size,
181 std::vector<Rect>& glyph_positions,
182 const std::vector<Rect>& glyph_sizes,
183 int64_t height_adjustment,
184 const std::shared_ptr<RectanglePacker>& rect_packer,
185 size_t start_index) {
188 for (
size_t i = start_index;
i < pairs.size();
i++) {
198 location_in_atlas.
x() + 1,
199 location_in_atlas.
y() + height_adjustment + 1,
209 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
210 const std::vector<FontGlyphPair>& extra_pairs,
211 std::vector<Rect>& glyph_positions,
212 const std::vector<Rect>& glyph_sizes,
213 size_t glyph_index_start,
214 int64_t max_texture_height) {
217 static constexpr int64_t kAtlasWidth = 4096;
218 static constexpr int64_t kMinAtlasHeight = 1024;
220 ISize current_size =
ISize(kAtlasWidth, kMinAtlasHeight);
221 if (atlas_context->GetAtlasSize().height > current_size.
height) {
222 current_size.
height = atlas_context->GetAtlasSize().height * 2;
225 auto height_adjustment = atlas_context->GetAtlasSize().height;
226 while (current_size.
height <= max_texture_height) {
227 std::shared_ptr<RectanglePacker> rect_packer;
228 if (atlas_context->GetRectPacker() || glyph_index_start) {
231 current_size.
height - atlas_context->GetAtlasSize().height);
235 glyph_positions.erase(glyph_positions.begin() + glyph_index_start,
236 glyph_positions.end());
237 atlas_context->UpdateRectPacker(rect_packer);
239 extra_pairs, current_size, glyph_positions, glyph_sizes,
240 height_adjustment, rect_packer, glyph_index_start);
241 if (next_index == extra_pairs.size()) {
250 return Point((pos & 0xff) / 4.f, (pos >> 2 & 0xff) / 4.f);
254 const SkPoint position,
257 const Rect& scaled_bounds,
264 metrics.point_size, metrics.scaleX, metrics.skewX);
265 sk_font.setEdging(SkFont::Edging::kAntiAlias);
266 sk_font.setHinting(SkFontHinting::kSlight);
267 sk_font.setEmbolden(metrics.embolden);
268 sk_font.setSubpixel(
true);
269 sk_font.setSize(sk_font.getSize() *
static_cast<Scalar>(scaled_font.
scale));
273 glyph_color = SK_ColorBLACK;
275 glyph_color = SK_ColorWHITE;
282 glyph_paint.setColor(glyph_color);
283 glyph_paint.setBlendMode(SkBlendMode::kSrc);
284 if (prop.
stroke.has_value()) {
285 auto stroke = prop.
stroke;
286 glyph_paint.setStroke(
true);
287 glyph_paint.setStrokeWidth(stroke->width *
289 glyph_paint.setStrokeCap(ToSkiaCap(stroke->cap));
290 glyph_paint.setStrokeJoin(ToSkiaJoin(stroke->join));
291 glyph_paint.setStrokeMiter(stroke->miter_limit);
295 canvas->translate(subpixel_offset.
x, subpixel_offset.
y);
297 canvas->drawGlyphs({&glyph_id, 1u},
299 SkPoint::Make(-scaled_bounds.
GetLeft(),
311 std::shared_ptr<BlitPass>& blit_pass,
313 const std::shared_ptr<Texture>&
texture,
314 const std::vector<FontGlyphPair>& new_pairs,
319 bool has_light_glyphs =
320 HasLightGlyphs(atlas, new_pairs, start_index, end_index);
324 atlas,
Size(
texture->GetSize()), has_light_glyphs));
325 if (!
bitmap.tryAllocPixels()) {
333 auto canvas =
surface->getCanvas();
338 for (
size_t i = start_index;
i < end_index;
i++) {
341 if (!data.has_value()) {
344 auto [pos, bounds, placeholder] = data.value();
346 Size size = pos.GetSize();
347 if (size.IsEmpty()) {
351 DrawGlyph(canvas, SkPoint::Make(pos.GetLeft(), pos.GetTop()),
355 if (has_light_glyphs) {
356 auto a8_bitmap_status = ToA8Bitmap(
bitmap);
357 if (!a8_bitmap_status.ok()) {
361 bitmap = a8_bitmap_status.value();
370 atlas.
GetTexture()->GetTextureDescriptor().format),
373 return blit_pass->AddCopy(std::move(buffer_view),
380 std::shared_ptr<BlitPass>& blit_pass,
382 const std::shared_ptr<Texture>&
texture,
383 const std::vector<FontGlyphPair>& new_pairs,
388 for (
size_t i = start_index;
i < end_index;
i++) {
391 if (!data.has_value()) {
394 auto [pos, bounds, placeholder] = data.value();
397 Size size = pos.GetSize();
398 if (size.IsEmpty()) {
407 bool is_light_glyph =
412 if (!
bitmap.tryAllocPixels()) {
420 auto canvas =
surface->getCanvas();
428 if (is_light_glyph) {
429 auto a8_bitmap_status = ToA8Bitmap(
bitmap);
430 if (!a8_bitmap_status.ok()) {
434 bitmap = a8_bitmap_status.value();
442 atlas.
GetTexture()->GetTextureDescriptor().format),
448 if (!blit_pass->AddCopy(std::move(buffer_view),
451 size.width, size.height),
460 return blit_pass->ConvertTextureToShaderRead(
texture);
466 SkRect scaled_bounds;
469 glyph_paint.setStroke(
true);
476 font.getBounds({&glyph.
glyph.
index, 1}, {&scaled_bounds, 1}, &glyph_paint);
483 return Rect::MakeLTRB(scaled_bounds.fLeft - adjustment, scaled_bounds.fTop,
484 scaled_bounds.fRight + adjustment,
485 scaled_bounds.fBottom);
488std::pair<std::vector<FontGlyphPair>, std::vector<Rect>>
489TypographerContextSkia::CollectNewGlyphs(
490 const std::shared_ptr<GlyphAtlas>& atlas,
491 const std::vector<RenderableText>& renderable_texts) {
492 std::vector<FontGlyphPair> new_glyphs;
493 std::vector<Rect> glyph_sizes;
494 for (
const auto& frame : renderable_texts) {
496 frame.origin_transform.GetMaxBasisLengthXY());
497 for (
const auto& run : frame.text_frame->GetRuns()) {
498 auto metrics = run.GetFont().GetMetrics();
500 ScaledFont scaled_font{.font = run.GetFont(), .scale = rounded_scale};
502 FontGlyphAtlas* font_glyph_atlas =
503 atlas->GetOrCreateFontGlyphAtlas(scaled_font);
508 metrics.point_size, metrics.scaleX, metrics.skewX);
509 sk_font.setEdging(SkFont::Edging::kAntiAlias);
510 sk_font.setHinting(SkFontHinting::kSlight);
511 sk_font.setEmbolden(metrics.embolden);
515 sk_font.setSize(sk_font.getSize() *
516 static_cast<Scalar>(scaled_font.scale));
517 sk_font.setSubpixel(
true);
519 for (
const auto& glyph_position : run.GetGlyphPositions()) {
521 glyph_position, scaled_font.font.GetAxisAlignment(),
522 frame.origin_transform);
523 SubpixelGlyph subpixel_glyph(glyph_position.glyph, subpixel,
525 const auto& font_glyph_bounds =
526 font_glyph_atlas->FindGlyphBounds(subpixel_glyph);
528 if (!font_glyph_bounds.has_value()) {
529 new_glyphs.push_back(FontGlyphPair{scaled_font, subpixel_glyph});
531 sk_font, subpixel_glyph,
static_cast<Scalar>(scaled_font.scale));
532 glyph_sizes.push_back(glyph_bounds);
534 auto frame_bounds = FrameBounds{
540 font_glyph_atlas->AppendGlyph(subpixel_glyph, frame_bounds);
545 return {std::move(new_glyphs), std::move(glyph_sizes)};
552 const std::shared_ptr<GlyphAtlasContext>& atlas_context,
553 const std::vector<RenderableText>& renderable_texts)
const {
558 std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
561 if (renderable_texts.empty()) {
570 auto [new_glyphs, glyph_sizes] =
571 CollectNewGlyphs(last_atlas, renderable_texts);
572 if (new_glyphs.size() == 0) {
580 std::vector<Rect> glyph_positions;
581 glyph_positions.reserve(new_glyphs.size());
582 size_t first_missing_index = 0;
584 if (last_atlas->GetTexture()) {
587 last_atlas, new_glyphs, glyph_positions, glyph_sizes,
588 atlas_context->GetAtlasSize(), atlas_context->GetHeightAdjustment(),
589 atlas_context->GetRectPacker());
595 for (
size_t i = 0;
i < first_missing_index;
i++) {
596 last_atlas->AddTypefaceGlyphPositionAndBounds(
597 new_glyphs[
i], glyph_positions[
i], glyph_sizes[
i]);
601 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
604 blit_pass->EncodeCommands();
606 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
615 last_atlas->GetTexture(), new_glyphs, 0,
616 first_missing_index)) {
621 if (first_missing_index == new_glyphs.size()) {
626 int64_t height_adjustment = atlas_context->GetAtlasSize().height;
627 const int64_t max_texture_height =
635 bool blit_old_atlas =
true;
636 std::shared_ptr<GlyphAtlas> new_atlas = last_atlas;
637 if (atlas_context->GetAtlasSize().height >= max_texture_height ||
639 blit_old_atlas =
false;
640 new_atlas = std::make_shared<GlyphAtlas>(
641 type, last_atlas->GetAtlasGeneration() + 1);
643 auto [update_glyphs, update_sizes] =
644 CollectNewGlyphs(new_atlas, renderable_texts);
645 new_glyphs = std::move(update_glyphs);
646 glyph_sizes = std::move(update_sizes);
648 glyph_positions.clear();
649 glyph_positions.reserve(new_glyphs.size());
650 first_missing_index = 0;
652 height_adjustment = 0;
653 atlas_context->UpdateRectPacker(
nullptr);
654 atlas_context->UpdateGlyphAtlas(new_atlas, {0, 0}, 0);
666 atlas_context->UpdateGlyphAtlas(new_atlas, atlas_size, height_adjustment);
670 FML_DCHECK(new_glyphs.size() == glyph_positions.size());
682 descriptor.
size = atlas_size;
685 std::shared_ptr<Texture> new_texture =
691 new_texture->SetLabel(
"GlyphAtlas");
694 std::shared_ptr<BlitPass> blit_pass = cmd_buffer->CreateBlitPass();
697 blit_pass->EncodeCommands();
699 VALIDATION_LOG <<
"Failed to submit glyph atlas command buffer";
704 auto old_texture = new_atlas->GetTexture();
705 new_atlas->SetTexture(std::move(new_texture));
711 for (
size_t i = first_missing_index;
i < glyph_positions.size();
i++) {
712 new_atlas->AddTypefaceGlyphPositionAndBounds(
713 new_glyphs[
i], glyph_positions[
i], glyph_sizes[
i]);
721 new_atlas->GetTexture(), new_glyphs,
722 first_missing_index, new_glyphs.size())) {
727 if (blit_old_atlas && old_texture) {
728 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
~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
static SkImageInfo GetImageInfo(const GlyphAtlas &atlas, Size size, bool support_light_glyphs)
std::shared_ptr< GlyphAtlasContext > CreateGlyphAtlasContext(GlyphAtlas::Type type) const override
static std::shared_ptr< TypographerContext > Make()
#define FML_UNREACHABLE()
#define FML_DCHECK(condition)
std::shared_ptr< SkBitmap > bitmap
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 GlyphProperties &prop)
static Point SubpixelPositionToPoint(SubpixelPosition pos)
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.
impeller::ShaderType type
A font along with a glyph in that font rendered at a particular scale and subpixel position.
static constexpr ToneOrColor kLightTone
static constexpr ToneOrColor kDarkTone
ToneOrColor tone_or_color
std::optional< StrokeParameters > stroke
A font and a scale. Used as a key that represents a typeface within a glyph atlas.
A glyph and its subpixel position.
SubpixelPosition subpixel_offset
GlyphProperties properties
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...
#define TRACE_EVENT0(category_group, name)