blob: 11c256a8af9184a3afac493a457860769f7a0df0 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/typographer/backends/skia/text_frame_skia.h"
#include <vector>
#include "flutter/fml/logging.h"
#include "impeller/typographer/backends/skia/typeface_skia.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkRect.h"
#include "third_party/skia/include/core/SkFont.h"
#include "third_party/skia/include/core/SkFontMetrics.h"
#include "third_party/skia/src/core/SkStrikeSpec.h" // nogncheck
#include "third_party/skia/src/core/SkTextBlobPriv.h" // nogncheck
namespace impeller {
static Font ToFont(const SkTextBlobRunIterator& run) {
auto& font = run.font();
auto typeface = std::make_shared<TypefaceSkia>(font.refTypefaceOrDefault());
SkFontMetrics sk_metrics;
font.getMetrics(&sk_metrics);
Font::Metrics metrics;
metrics.point_size = font.getSize();
metrics.embolden = font.isEmbolden();
metrics.skewX = font.getSkewX();
metrics.scaleX = font.getScaleX();
return Font{std::move(typeface), metrics};
}
static Rect ToRect(const SkRect& rect) {
return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
}
static constexpr Scalar kScaleSize = 100000.0f;
std::optional<TextFrame> MakeTextFrameFromTextBlobSkia(
const sk_sp<SkTextBlob>& blob) {
// Handling nullptr text blobs feels overly defensive here, as I don't
// actually know if this happens.
if (!blob) {
return {};
}
std::vector<TextRun> runs;
bool has_color = false;
for (SkTextBlobRunIterator run(blob.get()); !run.done(); run.next()) {
// TODO(jonahwilliams): ask Skia for a public API to look this up.
// https://github.com/flutter/flutter/issues/112005
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(run.font());
SkBulkGlyphMetricsAndPaths paths{strikeSpec};
const auto glyph_count = run.glyphCount();
const auto* glyphs = run.glyphs();
switch (run.positioning()) {
case SkTextBlobRunIterator::kFull_Positioning: {
std::vector<SkRect> glyph_bounds;
glyph_bounds.resize(glyph_count);
SkFont font = run.font();
auto font_size = font.getSize();
// For some platforms (including Android), `SkFont::getBounds()` snaps
// the computed bounds to integers. And so we scale up the font size
// prior to fetching the bounds to ensure that the returned bounds are
// always precise enough.
font.setSize(kScaleSize);
font.getBounds(glyphs, glyph_count, glyph_bounds.data(), nullptr);
std::vector<TextRun::GlyphPosition> positions;
positions.reserve(glyph_count);
for (auto i = 0u; i < glyph_count; i++) {
// kFull_Positioning has two scalars per glyph.
const SkPoint* glyph_points = run.points();
const auto* point = glyph_points + i;
Glyph::Type type = paths.glyph(glyphs[i])->isColor()
? Glyph::Type::kBitmap
: Glyph::Type::kPath;
has_color |= type == Glyph::Type::kBitmap;
positions.emplace_back(TextRun::GlyphPosition{
Glyph{glyphs[i], type,
ToRect(glyph_bounds[i]).Scale(font_size / kScaleSize)},
Point{point->x(), point->y()}});
}
TextRun text_run(ToFont(run), positions);
runs.emplace_back(text_run);
break;
}
default:
FML_DLOG(ERROR) << "Unimplemented.";
continue;
}
}
return TextFrame(runs, ToRect(blob->bounds()), has_color);
}
} // namespace impeller