mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-02 04:21:41 +01:00
The backend rasterizes coverage (or color, for emoji) as usual, then a shared CPU pass pads the bitmap, blurs an offset copy of the coverage and composites the text over it as RGBA8888. Blur and offset scale with the font size and with DPI. The flag is part of the string cache key, and the text is still aligned by its own size, with the quad grown to fit the shadow. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
186 lines
5.6 KiB
C++
186 lines
5.6 KiB
C++
// draw_text
|
|
|
|
// Uses system fonts to draw text.
|
|
// Platform support will be added over time, initially just Win32.
|
|
|
|
// Caches strings in individual textures. Might later combine them into a big one
|
|
// with dynamically allocated space but too much trouble for now.
|
|
|
|
#pragma once
|
|
|
|
#include "ppsspp_config.h"
|
|
|
|
#include <memory>
|
|
#include <cstdint>
|
|
#include <map>
|
|
#include <unordered_map>
|
|
|
|
#include "Common/Data/Text/WrapText.h"
|
|
#include "Common/Render/DrawBuffer.h"
|
|
#include "Common/Render/Text/Font.h"
|
|
|
|
namespace Draw {
|
|
class DrawContext;
|
|
class Texture;
|
|
}
|
|
|
|
struct TextStringEntry {
|
|
TextStringEntry(int frameCount) : lastUsedFrame(frameCount) {}
|
|
|
|
Draw::Texture *texture = nullptr;
|
|
int width = 0;
|
|
int height = 0;
|
|
int bmWidth = 0;
|
|
int bmHeight = 0;
|
|
// Padding in bitmap pixels around the text, on each side, to fit a drop shadow.
|
|
int shadowPad = 0;
|
|
int lastUsedFrame;
|
|
};
|
|
|
|
struct TextMeasureEntry {
|
|
int width;
|
|
int height;
|
|
int leading; // only used with Cocoa
|
|
int lastUsedFrame;
|
|
};
|
|
|
|
struct TextWrapEntry {
|
|
std::string wrapped;
|
|
int lastUsedFrame;
|
|
};
|
|
|
|
struct TextCacheKey {
|
|
std::string text;
|
|
FontStyle style;
|
|
int flags = 0;
|
|
// Wrapping only.
|
|
float maxWidth = 0.0f;
|
|
float scale = 0.0f;
|
|
|
|
bool operator==(const TextCacheKey &other) const {
|
|
return text == other.text && style == other.style && flags == other.flags && maxWidth == other.maxWidth && scale == other.scale;
|
|
}
|
|
};
|
|
|
|
struct TextCacheKeyHash {
|
|
size_t operator()(const TextCacheKey &key) const;
|
|
};
|
|
|
|
class TextDrawer {
|
|
public:
|
|
virtual ~TextDrawer() = default;
|
|
|
|
virtual bool IsReady() const { return true; }
|
|
virtual void SetOrCreateFont(const FontStyle &style) = 0;
|
|
|
|
void SetFontScale(float xscale, float yscale);
|
|
void MeasureString(std::string_view str, float *w, float *h);
|
|
void MeasureStringRect(std::string_view str, float maxWidth, float *w, float *h, int align = ALIGN_TOPLEFT);
|
|
|
|
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT);
|
|
void DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align);
|
|
virtual bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) = 0;
|
|
bool DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align, bool fullColor);
|
|
// Use for housekeeping like throwing out old strings.
|
|
void OncePerFrame();
|
|
|
|
float CalculateDPIScale() const;
|
|
// This is used by PPGe, since we run at the PSP's DPI inside of there.
|
|
void SetForcedDPIScale(float dpi) {
|
|
dpiScale_ = dpi;
|
|
ignoreGlobalDpi_ = true;
|
|
}
|
|
|
|
// Factory function that selects implementation.
|
|
static TextDrawer *Create(Draw::DrawContext *draw);
|
|
|
|
size_t GetStringCacheSize() const {
|
|
return cache_.size();
|
|
}
|
|
size_t GetCacheDataSize() const;
|
|
|
|
protected:
|
|
TextDrawer(Draw::DrawContext *draw);
|
|
|
|
// Implementations of this must handle multi-line strings.
|
|
virtual void MeasureStringInternal(std::string_view str, float *w, float *h) = 0;
|
|
|
|
void ClearCache();
|
|
|
|
virtual bool SupportsColorEmoji() const = 0;
|
|
virtual void ClearFonts() = 0;
|
|
|
|
// Wraps and/or ellipsizes, caching the result. The reference stays valid until the next OncePerFrame.
|
|
const std::string &WrapString(std::string_view str, float maxWidth, int flags);
|
|
|
|
// v has the 8-bit alpha.
|
|
static u16 AlphaToPremul4444(u32 v) {
|
|
v = (v >> 4) & 0x0F;
|
|
v |= v << 4;
|
|
v |= v << 8;
|
|
return v;
|
|
}
|
|
static u32 AlphaToPremul8888(u32 v) {
|
|
v |= v << 8;
|
|
v |= v << 16;
|
|
return v;
|
|
}
|
|
static u32 RGBAToPremul8888(u32 v) {
|
|
u32 a = (v >> 24) & 0xFF;
|
|
if (a == 0xFF)
|
|
return v;
|
|
if (a == 0)
|
|
return 0;
|
|
u32 r = (v >> 16) & 0xFF;
|
|
u32 g = (v >> 8) & 0xFF;
|
|
u32 b = v & 0xFF;
|
|
r = (r * a + 127) / 255;
|
|
g = (g * a + 127) / 255;
|
|
b = (b * a + 127) / 255;
|
|
return (a << 24) | (r << 16) | (g << 8) | b;
|
|
}
|
|
|
|
Draw::DrawContext *draw_;
|
|
|
|
int frameCount_ = 0;
|
|
float fontScaleX_ = 1.0f;
|
|
float fontScaleY_ = 1.0f;
|
|
float dpiScale_ = 1.0f;
|
|
bool ignoreGlobalDpi_ = false;
|
|
FontStyle fontStyle_{};
|
|
|
|
// We will clamp strings to this length to avoid various degenenerate behaviors.k
|
|
static constexpr size_t MAX_TEXT_LENGTH = 16384;
|
|
|
|
// Fills in and returns lookupKey_, which reuses its string buffer, so a cache hit doesn't allocate.
|
|
const TextCacheKey &LookupKey(std::string_view str, int flags = 0, float maxWidth = 0.0f, float scale = 0.0f);
|
|
void AddDropShadow(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat srcFormat) const;
|
|
|
|
TextCacheKey lookupKey_;
|
|
|
|
std::unordered_map<TextCacheKey, std::unique_ptr<TextStringEntry>, TextCacheKeyHash> cache_;
|
|
std::unordered_map<TextCacheKey, TextMeasureEntry, TextCacheKeyHash> sizeCache_;
|
|
std::unordered_map<TextCacheKey, TextWrapEntry, TextCacheKeyHash> wrapCache_;
|
|
};
|
|
|
|
class TextDrawerWordWrapper : public WordWrapper {
|
|
public:
|
|
TextDrawerWordWrapper(TextDrawer *drawer, std::string_view str, float maxW, int flags)
|
|
: WordWrapper(str, maxW, flags), drawer_(drawer) {}
|
|
|
|
protected:
|
|
float MeasureWidth(std::string_view str) override;
|
|
|
|
TextDrawer *drawer_;
|
|
};
|
|
|
|
// The backends can use this to query the filenames of the fonts.
|
|
// Some backends want to just load all the fonts, just pass all.
|
|
// Note that the ttf file extension is included in the output.
|
|
std::vector<std::string> GetAllFontFilenames();
|
|
std::string GetFilenameForFontStyle(const FontStyle &font);
|
|
std::string GetFontNameForFontStyle(const FontStyle &font, FontStyleFlags *outFlags);
|
|
|
|
// Some languages use an override font, set via ini file.
|
|
void SetFontNameOverride(FontFamily family, std::string_view overrideFont);
|