#pragma once #include #include #include #include #include #include "core/rendering/sprite/sprite.hpp" class Surface; // Bitmap font amb suport UTF-8. Els glifs es guarden en una única graella // al png, amb ample variable per caràcter i una tinta monocroma que es // recolora al vol via Sprite::render(src, tgt). class Text { public: struct Offset { int x{0}; int y{0}; int w{0}; }; struct File { int box_width{0}; int box_height{0}; int columns{16}; int cell_spacing{0}; int row_spacing{0}; std::unordered_map offset; }; // Flags per a writeDX (composables amb |). static constexpr Uint8 COLOR_FLAG = 1; static constexpr Uint8 SHADOW_FLAG = 2; static constexpr Uint8 CENTER_FLAG = 4; static constexpr Uint8 STROKE_FLAG = 8; // Índex del píxel "tinta" al PNG de la font. Els glifs són monocroms // sobre transparent i tots els píxels opacs usen aquest índex; la Surface // el recolora al vol per a emetre el color destí. static constexpr Uint8 INK_COLOR_INDEX = 1; // Color per defecte a la paleta quan s'usa write() sense especificar-lo. static constexpr Uint8 DEFAULT_TEXT_COLOR = 15; Text(const std::shared_ptr& surface, const std::string& text_file); Text(const std::shared_ptr& surface, const std::shared_ptr& text_file); ~Text() = default; // Kerning per defecte de la font (separació entre glifs en píxels, sumat // a l'amplada natural). Els callers poden passar un kerning explícit a // write/length, o llegir aquest valor i reenviar-lo; així cada font pot // tenir un kerning pròpi (p. ex. negatiu per a displays amb sidebearings // amples) sense obligar els callers a saber-ho. void setDefaultKerning(int kerning) { default_kerning_ = kerning; } [[nodiscard]] auto getDefaultKerning() const -> int { return default_kerning_; } Text(const Text&) = delete; Text(Text&&) = delete; auto operator=(const Text&) -> Text& = delete; auto operator=(Text&&) -> Text& = delete; void write(int x, int y, const std::string& text, int kerning = 1, int length = -1); void writeColored(int x, int y, const std::string& text, Uint8 color, int kerning = 1, int length = -1); void writeDX(Uint8 flags, int x, int y, const std::string& text, int kerning = 1, Uint8 text_color = 0, Uint8 shadow_distance = 1, Uint8 shadow_color = 0, int length = -1); // Dibuixa amb amplada fixa només per als dígits '0'-'9' (cadascun centrat // dins d'una cel·la de digit_cell_w). La resta de glifs mantenen amplada // natural. Evita jitter en comptadors tipus "9:59.59". void writeMonoDigits(int x, int y, const std::string& text, Uint8 color, int digit_cell_w, int kerning = 1); [[nodiscard]] auto length(const std::string& text, int kerning = 1) const -> int; [[nodiscard]] auto lengthMonoDigits(const std::string& text, int digit_cell_w, int kerning = 1) const -> int; [[nodiscard]] auto getCharacterSize() const -> int { return box_width_; } [[nodiscard]] auto getLineHeight() const -> int { return box_height_; } static auto loadTextFile(const std::string& file_path) -> std::shared_ptr; static auto nextCodepoint(const std::string& s, std::size_t& pos) -> std::uint32_t; private: // Busca l'Offset del codepoint; si no hi és, cau al glif '?' com a // substitut. Retorna nullptr si tampoc hi ha '?'. [[nodiscard]] auto resolveGlyph(std::uint32_t cp) const -> const Offset*; std::unique_ptr sprite_; int box_width_{0}; int box_height_{0}; int default_kerning_{1}; std::unordered_map offset_; };