明面上没bug了
This commit is contained in:
+104
-12
@@ -3,11 +3,59 @@
|
||||
#include "Font_Face_p.h"
|
||||
#include "Image_p.h"
|
||||
#include "blend2d/blend2d.h"
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <system_error>
|
||||
namespace renderive {
|
||||
|
||||
namespace {
|
||||
std::string& default_font_source_storage() {
|
||||
static std::string source;
|
||||
return source;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<Font_Face> Canvas::default_font_face() {
|
||||
static std::shared_ptr<Font_Face> face = []() -> std::shared_ptr<Font_Face> {
|
||||
static constexpr std::array<const char*, 5> candidates{
|
||||
"C:/Windows/Fonts/msyh.ttc",
|
||||
"C:/Windows/Fonts/simsun.ttc",
|
||||
"C:/Windows/Fonts/segoeui.ttf",
|
||||
"C:/Windows/Fonts/arial.ttf",
|
||||
"C:/Windows/Fonts/tahoma.ttf"
|
||||
};
|
||||
for (const char* path : candidates) {
|
||||
std::error_code error;
|
||||
if (!std::filesystem::exists(path, error))
|
||||
continue;
|
||||
auto loaded = std::make_shared<Font_Face>();
|
||||
if (loaded->impl_->face.create_from_file(path) == BL_SUCCESS) {
|
||||
default_font_source_storage() = path;
|
||||
return loaded;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}();
|
||||
return face;
|
||||
}
|
||||
|
||||
Font Canvas::resolve_font(Font font) {
|
||||
if (font.size <= 0.0)
|
||||
font.size = 12.0;
|
||||
if (!font.face || !font.face->valid())
|
||||
font.face = default_font_face();
|
||||
return font;
|
||||
}
|
||||
|
||||
bool Canvas::can_use_font(const Font& font) {
|
||||
return font.size > 0.0 && font.face && font.face->valid();
|
||||
}
|
||||
|
||||
struct Canvas::Impl {
|
||||
Image& image;
|
||||
BLContext ctx;
|
||||
Font current_font;
|
||||
Font current_font = Canvas::resolve_font({});
|
||||
bool antialias = true;
|
||||
Pen current_pen;
|
||||
Brush current_brush;
|
||||
@@ -29,6 +77,13 @@ struct Canvas::Impl {
|
||||
}
|
||||
ctx.set_fill_style(Convert::to_bl(current_brush.color));
|
||||
}
|
||||
void apply_text_fill() {
|
||||
if (!current_pen.enabled()) {
|
||||
ctx.set_fill_style(BLRgba(0.0, 0.0, 0.0, 0.0));
|
||||
return;
|
||||
}
|
||||
ctx.set_fill_style(Convert::to_bl(current_pen.color));
|
||||
}
|
||||
void apply_pen(double stroke_width = -1.0) {
|
||||
if (!current_pen.enabled()) {
|
||||
ctx.set_stroke_style(BLRgba(0.0, 0.0, 0.0, 0.0));
|
||||
@@ -71,7 +126,7 @@ void Canvas::set_brush(const Brush& brush) {
|
||||
impl_->current_brush = brush;
|
||||
}
|
||||
void Canvas::set_font(const Font& font) {
|
||||
impl_->current_font = font;
|
||||
impl_->current_font = resolve_font(font);
|
||||
}
|
||||
void Canvas::set_antialias(bool enabled) {
|
||||
impl_->antialias = enabled;
|
||||
@@ -180,16 +235,17 @@ void Canvas::draw_image(const RectF& target, const Image& image, const RectF& so
|
||||
static_cast<int>(source.width), static_cast<int>(source.height)));
|
||||
}
|
||||
void Canvas::draw_text(PointF position, std::string_view text) {
|
||||
if (text.empty() || !impl_->current_font.valid())
|
||||
if (text.empty() || !can_use_font(impl_->current_font))
|
||||
return;
|
||||
BLFont bl_font;
|
||||
bl_font.create_from_face(impl_->current_font.face->impl_->face, static_cast<float>(impl_->current_font.size));
|
||||
impl_->apply_brush();
|
||||
const BLFontMetrics& metrics = bl_font.metrics();
|
||||
impl_->apply_text_fill();
|
||||
impl_->ctx.set_stroke_style(BLRgba(0.0, 0.0, 0.0, 0.0));
|
||||
impl_->ctx.fill_utf8_text(BLPoint(position.x, position.y + impl_->current_font.size), bl_font, text.data(), text.size());
|
||||
impl_->ctx.fill_utf8_text(BLPoint(position.x, position.y + metrics.ascent), bl_font, text.data(), text.size());
|
||||
}
|
||||
void Canvas::draw_text(const RectF& rect, Text_Align align, std::string_view text) {
|
||||
if (text.empty() || !impl_->current_font.valid())
|
||||
if (text.empty() || !can_use_font(impl_->current_font))
|
||||
return;
|
||||
auto metrics = measure_text(text);
|
||||
double x = rect.x;
|
||||
@@ -199,21 +255,20 @@ void Canvas::draw_text(const RectF& rect, Text_Align align, std::string_view tex
|
||||
else if ((align & Text_Align::Right) == Text_Align::Right)
|
||||
x += rect.width - metrics.width;
|
||||
if ((align & Text_Align::VCenter) == Text_Align::VCenter)
|
||||
y += (rect.height - metrics.height) / 2.0 + metrics.ascent;
|
||||
y += (rect.height - metrics.height) / 2.0;
|
||||
else if ((align & Text_Align::Bottom) == Text_Align::Bottom)
|
||||
y += rect.height - metrics.descent;
|
||||
else
|
||||
y += metrics.ascent;
|
||||
y += rect.height - metrics.height;
|
||||
draw_text(PointF{x, y}, text);
|
||||
}
|
||||
Text_Metrics Canvas::measure_text(std::string_view text) const {
|
||||
return measure_text(impl_->current_font, text);
|
||||
}
|
||||
Text_Metrics Canvas::measure_text(const Font& font, std::string_view text) const {
|
||||
if (text.empty() || !font.valid())
|
||||
Font resolved = resolve_font(font);
|
||||
if (text.empty() || !can_use_font(resolved))
|
||||
return {};
|
||||
BLFont bl_font;
|
||||
bl_font.create_from_face(font.face->impl_->face, static_cast<float>(font.size));
|
||||
bl_font.create_from_face(resolved.face->impl_->face, static_cast<float>(resolved.size));
|
||||
BLGlyphBuffer gb;
|
||||
gb.set_utf8_text(text.data(), text.size());
|
||||
bl_font.shape(gb);
|
||||
@@ -231,4 +286,41 @@ Text_Metrics Canvas::measure_text(const Font& font, std::string_view text) const
|
||||
Image& Canvas::image() const {
|
||||
return impl_->image;
|
||||
}
|
||||
|
||||
Canvas::Font_Diagnostics Canvas::default_font_diagnostics() {
|
||||
Font_Diagnostics diagnostics;
|
||||
auto face = default_font_face();
|
||||
diagnostics.source = default_font_source_storage();
|
||||
diagnostics.face_valid = face && face->valid();
|
||||
Font font;
|
||||
font.face = face;
|
||||
font.size = 12.0;
|
||||
diagnostics.font_valid = can_use_font(font);
|
||||
if (!diagnostics.font_valid)
|
||||
return diagnostics;
|
||||
|
||||
Image image(1, 1);
|
||||
image.fill(Color::transparent());
|
||||
Canvas canvas(image);
|
||||
canvas.set_font(font);
|
||||
Text_Metrics metrics = canvas.measure_text("0123456789 ABC \xE4\xB8\xAD\xE6\x96\x87");
|
||||
diagnostics.test_width = metrics.width;
|
||||
diagnostics.test_height = metrics.height;
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
void Canvas::diagnose_default_font_once() {
|
||||
static bool done = false;
|
||||
if (done)
|
||||
return;
|
||||
done = true;
|
||||
Font_Diagnostics diagnostics = default_font_diagnostics();
|
||||
std::cout << "Renderive default font: source="
|
||||
<< (diagnostics.source.empty() ? "<none>" : diagnostics.source)
|
||||
<< " face_valid=" << (diagnostics.face_valid ? "true" : "false")
|
||||
<< " font_valid=" << (diagnostics.font_valid ? "true" : "false")
|
||||
<< " test_width=" << diagnostics.test_width
|
||||
<< " test_height=" << diagnostics.test_height
|
||||
<< std::endl;
|
||||
}
|
||||
} // namespace renderive
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Image_View.h"
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace renderive {
|
||||
@@ -16,6 +17,14 @@ class Font_Face;
|
||||
|
||||
class Canvas {
|
||||
public:
|
||||
struct Font_Diagnostics {
|
||||
bool face_valid{};
|
||||
bool font_valid{};
|
||||
double test_width{};
|
||||
double test_height{};
|
||||
std::string source;
|
||||
};
|
||||
|
||||
explicit Canvas(Image& image);
|
||||
~Canvas();
|
||||
|
||||
@@ -58,7 +67,14 @@ public:
|
||||
|
||||
Image& image() const;
|
||||
|
||||
[[nodiscard]] static Font_Diagnostics default_font_diagnostics();
|
||||
static void diagnose_default_font_once();
|
||||
|
||||
private:
|
||||
static std::shared_ptr<Font_Face> default_font_face();
|
||||
static Font resolve_font(Font font);
|
||||
static bool can_use_font(const Font& font);
|
||||
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user