mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #19219 from hrydgard/more-text-rendering-work
Unify DrawString between Windows, UWP and Android. More to come.
This commit is contained in:
commit
a431d53da3
15 changed files with 156 additions and 433 deletions
|
@ -47,6 +47,82 @@ float TextDrawer::CalculateDPIScale() {
|
|||
return scale;
|
||||
}
|
||||
|
||||
void TextDrawer::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {
|
||||
using namespace Draw;
|
||||
if (str.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
|
||||
auto iter = cache_.find(key);
|
||||
if (iter != cache_.end()) {
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
if (!entry->texture) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
DataFormat texFormat;
|
||||
// Pick between the supported formats, of which at least one is supported on each platform. Prefer R8 (but only if swizzle is supported)
|
||||
bool emoji = SupportsColorEmoji() && AnyEmojiInString(str.data(), str.length());
|
||||
if (emoji) {
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
} else if ((draw_->GetDataFormatSupport(Draw::DataFormat::R8_UNORM) & Draw::FMT_TEXTURE) != 0 && draw_->GetDeviceCaps().textureSwizzleSupported) {
|
||||
texFormat = Draw::DataFormat::R8_UNORM;
|
||||
} else if (draw_->GetDataFormatSupport(Draw::DataFormat::R4G4B4A4_UNORM_PACK16) & FMT_TEXTURE) {
|
||||
texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
|
||||
} else if (draw_->GetDataFormatSupport(Draw::DataFormat::A4R4G4B4_UNORM_PACK16) & FMT_TEXTURE) {
|
||||
texFormat = Draw::DataFormat::A4R4G4B4_UNORM_PACK16;
|
||||
} else if (draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16) & FMT_TEXTURE) {
|
||||
texFormat = Draw::DataFormat::B4G4R4A4_UNORM_PACK16;
|
||||
} else {
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
entry = new TextStringEntry(frameCount_);
|
||||
|
||||
// Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format
|
||||
// because we need white. Well, we could using swizzle, but not all our backends support that.
|
||||
TextureDesc desc{};
|
||||
std::vector<uint8_t> bitmapData;
|
||||
if (!DrawStringBitmap(bitmapData, *entry, texFormat, str, align, emoji)) {
|
||||
// Nothing drawn. Store that fact in the cache.
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
desc.initData.push_back(&bitmapData[0]);
|
||||
|
||||
desc.type = TextureType::LINEAR2D;
|
||||
desc.format = texFormat;
|
||||
desc.width = entry->bmWidth;
|
||||
desc.height = entry->bmHeight;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.tag = "TextDrawer";
|
||||
desc.swizzle = texFormat == Draw::DataFormat::R8_UNORM ? Draw::TextureSwizzle::R8_AS_ALPHA : Draw::TextureSwizzle::DEFAULT,
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
}
|
||||
|
||||
_dbg_assert_(entry->texture);
|
||||
draw_->BindTexture(0, entry->texture);
|
||||
|
||||
// Okay, the texture is bound, let's draw.
|
||||
float w = entry->width * fontScaleX_ * dpiScale_;
|
||||
float h = entry->height * fontScaleY_ * dpiScale_;
|
||||
float u = entry->width / (float)entry->bmWidth;
|
||||
float v = entry->height / (float)entry->bmHeight;
|
||||
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
|
||||
|
||||
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, u, v, color);
|
||||
target.Flush(true);
|
||||
}
|
||||
|
||||
void TextDrawer::DrawStringRect(DrawBuffer &target, std::string_view str, const Bounds &bounds, uint32_t color, int align) {
|
||||
float x = bounds.x;
|
||||
float y = bounds.y;
|
||||
|
@ -71,15 +147,14 @@ void TextDrawer::DrawStringRect(DrawBuffer &target, std::string_view str, const
|
|||
DrawString(target, toDraw.c_str(), x, y, color, align);
|
||||
}
|
||||
|
||||
bool TextDrawer::DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align) {
|
||||
bool TextDrawer::DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, int align, bool fullColor) {
|
||||
std::string toDraw(str);
|
||||
int wrap = align & (FLAG_WRAP_TEXT | FLAG_ELLIPSIZE_TEXT);
|
||||
if (wrap) {
|
||||
bool rotated = (align & (ROTATE_90DEG_LEFT | ROTATE_90DEG_RIGHT)) != 0;
|
||||
WrapString(toDraw, str, rotated ? bounds.h : bounds.w, wrap);
|
||||
}
|
||||
|
||||
return DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align);
|
||||
return DrawStringBitmap(bitmapData, entry, texFormat, toDraw.c_str(), align, fullColor);
|
||||
}
|
||||
|
||||
TextDrawer *TextDrawer::Create(Draw::DrawContext *draw) {
|
||||
|
|
|
@ -51,13 +51,10 @@ public:
|
|||
virtual void MeasureString(std::string_view str, float *w, float *h) = 0;
|
||||
virtual void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) = 0;
|
||||
|
||||
// TODO: This one we should be able to make a default implementation for, calling the specialized DrawBitmap.
|
||||
// Only problem is that we need to make sure that the texFormats are all supported by all the backends, or we explicitly limit.
|
||||
virtual void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) = 0;
|
||||
|
||||
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 = ALIGN_TOPLEFT) = 0;
|
||||
bool DrawStringBitmapRect(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, const Bounds &bounds, 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.
|
||||
virtual void OncePerFrame() = 0;
|
||||
|
||||
|
@ -73,8 +70,9 @@ public:
|
|||
protected:
|
||||
TextDrawer(Draw::DrawContext *draw);
|
||||
|
||||
Draw::DrawContext *draw_;
|
||||
virtual bool SupportsColorEmoji() const = 0;
|
||||
virtual void ClearCache() = 0;
|
||||
|
||||
void WrapString(std::string &out, std::string_view str, float maxWidth, int flags);
|
||||
|
||||
struct CacheKey {
|
||||
|
@ -89,12 +87,16 @@ protected:
|
|||
uint32_t fontHash;
|
||||
};
|
||||
|
||||
Draw::DrawContext *draw_;
|
||||
|
||||
int frameCount_ = 0;
|
||||
float fontScaleX_ = 1.0f;
|
||||
float fontScaleY_ = 1.0f;
|
||||
float dpiScale_ = 1.0f;
|
||||
bool ignoreGlobalDpi_ = false;
|
||||
|
||||
uint32_t fontHash_ = 0;
|
||||
|
||||
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
|
||||
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
|
||||
};
|
||||
|
|
|
@ -29,11 +29,7 @@ TextDrawerAndroid::TextDrawerAndroid(Draw::DrawContext *draw) : TextDrawer(draw)
|
|||
}
|
||||
dpiScale_ = CalculateDPIScale();
|
||||
|
||||
// Pick between the two supported formats, of which at least one is supported on each platform. Prefer R8 (but only if swizzle is supported)
|
||||
use4444Format_ = (draw->GetDataFormatSupport(Draw::DataFormat::R4G4B4A4_UNORM_PACK16) & Draw::FMT_TEXTURE) != 0;
|
||||
if ((draw->GetDataFormatSupport(Draw::DataFormat::R8_UNORM) & Draw::FMT_TEXTURE) != 0 && draw->GetDeviceCaps().textureSwizzleSupported)
|
||||
use4444Format_ = false;
|
||||
INFO_LOG(G3D, "Initializing TextDrawerAndroid with DPI scale %f, use4444=%d", dpiScale_, (int)use4444Format_);
|
||||
INFO_LOG(G3D, "Initializing TextDrawerAndroid with DPI scale %f", dpiScale_);
|
||||
}
|
||||
|
||||
TextDrawerAndroid::~TextDrawerAndroid() {
|
||||
|
@ -171,7 +167,7 @@ void TextDrawerAndroid::MeasureStringRect(std::string_view str, const Bounds &bo
|
|||
*h = total_h * fontScaleY_ * dpiScale_;
|
||||
}
|
||||
|
||||
bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
|
||||
bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
if (str.empty()) {
|
||||
bitmapData.clear();
|
||||
return false;
|
||||
|
@ -244,60 +240,6 @@ bool TextDrawerAndroid::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextS
|
|||
return true;
|
||||
}
|
||||
|
||||
void TextDrawerAndroid::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {
|
||||
using namespace Draw;
|
||||
if (str.empty())
|
||||
return;
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
|
||||
auto iter = cache_.find(key);
|
||||
if (iter != cache_.end()) {
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
} else {
|
||||
DataFormat texFormat = use4444Format_ ? Draw::DataFormat::R4G4B4A4_UNORM_PACK16 : Draw::DataFormat::R8_UNORM;
|
||||
bool emoji = AnyEmojiInString(str.data(), str.length());
|
||||
if (emoji) {
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
entry = new TextStringEntry(frameCount_);
|
||||
|
||||
TextureDesc desc{};
|
||||
std::vector<uint8_t> bitmapData;
|
||||
DrawStringBitmap(bitmapData, *entry, texFormat, str, align);
|
||||
desc.initData.push_back(&bitmapData[0]);
|
||||
|
||||
desc.type = TextureType::LINEAR2D;
|
||||
desc.format = texFormat;
|
||||
desc.width = entry->bmWidth;
|
||||
desc.height = entry->bmHeight;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.generateMips = false;
|
||||
desc.swizzle = texFormat == Draw::DataFormat::R8_UNORM ? Draw::TextureSwizzle::R8_AS_ALPHA : Draw::TextureSwizzle::DEFAULT,
|
||||
desc.tag = "TextDrawer";
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
}
|
||||
|
||||
if (entry->texture) {
|
||||
draw_->BindTexture(0, entry->texture);
|
||||
}
|
||||
|
||||
float w = entry->bmWidth * fontScaleX_ * dpiScale_;
|
||||
float h = entry->bmHeight * fontScaleY_ * dpiScale_;
|
||||
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
|
||||
if (entry->texture) {
|
||||
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color);
|
||||
target.Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDrawerAndroid::ClearCache() {
|
||||
for (auto &iter : cache_) {
|
||||
if (iter.second->texture)
|
||||
|
|
|
@ -23,12 +23,13 @@ public:
|
|||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void MeasureString(std::string_view str, float *w, float *h) override;
|
||||
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
|
||||
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
// Use for housekeeping like throwing out old strings.
|
||||
void OncePerFrame() override;
|
||||
|
||||
protected:
|
||||
bool SupportsColorEmoji() const { return true; }
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
private:
|
||||
|
@ -37,9 +38,6 @@ private:
|
|||
jmethodID method_measureText;
|
||||
jmethodID method_renderText;
|
||||
|
||||
uint32_t fontHash_;
|
||||
bool use4444Format_ = false;
|
||||
|
||||
std::map<uint32_t, AndroidFontEntry> fontMap_;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,21 +20,18 @@ public:
|
|||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void MeasureString(std::string_view str, float *w, float *h) override;
|
||||
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
|
||||
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
// Use for housekeeping like throwing out old strings.
|
||||
void OncePerFrame() override;
|
||||
|
||||
protected:
|
||||
bool SupportsColorEmoji() const override { return true; }
|
||||
|
||||
void ClearCache() override;
|
||||
void RecreateFonts(); // On DPI change
|
||||
|
||||
TextDrawerContext *ctx_;
|
||||
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
|
||||
uint32_t fontHash_;
|
||||
std::map<CacheKey, std::unique_ptr<TextStringEntry>> cache_;
|
||||
std::map<CacheKey, std::unique_ptr<TextMeasureEntry>> sizeCache_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -140,10 +140,8 @@ void TextDrawerCocoa::MeasureString(std::string_view str, float *w, float *h) {
|
|||
attributes = iter->second->attributes;
|
||||
}
|
||||
|
||||
std::string toMeasure = ReplaceAll(std::string(str), "&&", "&");
|
||||
|
||||
std::vector<std::string_view> lines;
|
||||
SplitString(toMeasure, '\n', lines);
|
||||
SplitString(str, '\n', lines);
|
||||
|
||||
int extW = 0, extH = 0;
|
||||
for (auto &line : lines) {
|
||||
|
@ -172,7 +170,6 @@ void TextDrawerCocoa::MeasureString(std::string_view str, float *w, float *h) {
|
|||
*h = entry->height * fontScaleY_ * dpiScale_;
|
||||
}
|
||||
|
||||
|
||||
void TextDrawerCocoa::MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align) {
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
NSDictionary *attributes = nil;
|
||||
|
@ -228,76 +225,7 @@ void TextDrawerCocoa::MeasureStringRect(std::string_view str, const Bounds &boun
|
|||
*h = total_h * fontScaleY_ * dpiScale_;
|
||||
}
|
||||
|
||||
void TextDrawerCocoa::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {
|
||||
using namespace Draw;
|
||||
if (str.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
|
||||
auto iter = cache_.find(key);
|
||||
if (iter != cache_.end()) {
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
} else {
|
||||
DataFormat texFormat;
|
||||
// For our purposes these are equivalent, so just choose the supported one. D3D can emulate them.
|
||||
if (draw_->GetDataFormatSupport(Draw::DataFormat::A4R4G4B4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::A4R4G4B4_UNORM_PACK16;
|
||||
else if (draw_->GetDataFormatSupport(Draw::DataFormat::R4G4B4A4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
|
||||
else if (draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::B4G4R4A4_UNORM_PACK16;
|
||||
else
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
|
||||
entry = new TextStringEntry(frameCount_);
|
||||
|
||||
bool emoji = AnyEmojiInString(key.text.c_str(), key.text.size());
|
||||
if (emoji)
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
|
||||
// Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format
|
||||
// because we need white. Well, we could using swizzle, but not all our backends support that.
|
||||
TextureDesc desc{};
|
||||
std::vector<uint8_t> bitmapData;
|
||||
if (!DrawStringBitmap(bitmapData, *entry, texFormat, str, align)) {
|
||||
return;
|
||||
}
|
||||
desc.initData.push_back(&bitmapData[0]);
|
||||
|
||||
desc.type = TextureType::LINEAR2D;
|
||||
desc.format = texFormat;
|
||||
desc.width = entry->bmWidth;
|
||||
desc.height = entry->bmHeight;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.tag = "TextDrawer";
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
}
|
||||
|
||||
if (entry->texture) {
|
||||
draw_->BindTexture(0, entry->texture);
|
||||
}
|
||||
|
||||
// Okay, the texture is bound, let's draw.
|
||||
float w = entry->width * fontScaleX_ * dpiScale_;
|
||||
float h = entry->height * fontScaleY_ * dpiScale_;
|
||||
float u = entry->width / (float)entry->bmWidth;
|
||||
float v = entry->height / (float)entry->bmHeight;
|
||||
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
|
||||
if (entry->texture) {
|
||||
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, u, v, color);
|
||||
target.Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
|
||||
bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
if (str.empty()) {
|
||||
bitmapData.clear();
|
||||
return false;
|
||||
|
@ -319,25 +247,25 @@ bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
|||
double fWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
|
||||
|
||||
// On iOS 4.0 and Mac OS X v10.6 you can pass null for data
|
||||
size_t width = (size_t)ceilf(fWidth) + 1;
|
||||
size_t height = (size_t)ceilf(ascent + descent) + 1;
|
||||
int width = (int)ceilf(fWidth);
|
||||
int height = (int)ceilf(ascent + descent);
|
||||
|
||||
// Round width and height upwards to the closest multiple of 4.
|
||||
width = (width + 3) & ~3;
|
||||
height = (height + 3) & ~3;
|
||||
|
||||
if (!width || !height) {
|
||||
if (width <= 0 || height <= 0) {
|
||||
WARN_LOG(G3D, "Text '%.*s' caused a zero size image", (int)str.length(), str.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t *bitmap = new uint32_t[width * height];
|
||||
memset(bitmap, 0, width * height * 4);
|
||||
// Round width and height upwards to the closest multiple of 4.
|
||||
int bmWidth = (width + 1 + 3) & ~3;
|
||||
int bmHeight = (height + 1 + 3) & ~3;
|
||||
|
||||
uint32_t *bitmap = new uint32_t[bmWidth * bmHeight];
|
||||
memset(bitmap, 0, bmWidth * bmHeight * 4);
|
||||
|
||||
// Create the context and fill it with white background
|
||||
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast;
|
||||
CGContextRef ctx = CGBitmapContextCreate(bitmap, width, height, 8, width*4, space, bitmapInfo);
|
||||
CGContextRef ctx = CGBitmapContextCreate(bitmap, bmWidth, bmHeight, 8, bmWidth*4, space, bitmapInfo);
|
||||
CGColorSpaceRelease(space);
|
||||
// CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 0.0); // white background
|
||||
// CGContextFillRect(ctx, CGRectMake(0.0, 0.0, width, height));
|
||||
|
@ -348,7 +276,7 @@ bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
|||
|
||||
// Draw the text
|
||||
CGFloat x = 0.0;
|
||||
CGFloat y = descent;
|
||||
CGFloat y = descent + (bmHeight - height); // from bottom???
|
||||
CGContextSetTextPosition(ctx, x, y);
|
||||
CTLineDraw(line, ctx);
|
||||
CFRelease(line);
|
||||
|
@ -356,8 +284,8 @@ bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
|||
entry.texture = nullptr;
|
||||
entry.width = width;
|
||||
entry.height = height;
|
||||
entry.bmWidth = width;
|
||||
entry.bmHeight = height;
|
||||
entry.bmWidth = bmWidth;
|
||||
entry.bmHeight = bmHeight;
|
||||
entry.lastUsedFrame = frameCount_;
|
||||
|
||||
// data now contains the bytes in RGBA, presumably.
|
||||
|
@ -365,37 +293,44 @@ bool TextDrawerCocoa::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
|||
// because we need white. Well, we could using swizzle, but not all our backends support that.
|
||||
if (texFormat == Draw::DataFormat::R8G8B8A8_UNORM || texFormat == Draw::DataFormat::B8G8R8A8_UNORM) {
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint32_t));
|
||||
// If we chose this format, emoji are involved. Pass straight through.
|
||||
uint32_t *bitmapData32 = (uint32_t *)&bitmapData[0];
|
||||
for (int y = 0; y < entry.bmHeight; y++) {
|
||||
for (int x = 0; x < entry.bmWidth; x++) {
|
||||
uint32_t color = bitmap[width * y + x];
|
||||
bitmapData32[entry.bmWidth * y + x] = color;
|
||||
uint32_t color = bitmap[bmWidth * y + x];
|
||||
if (fullColor) {
|
||||
bitmapData32[entry.bmWidth * y + x] = color;
|
||||
} else {
|
||||
// Don't know why we'd end up here, but let's support it.
|
||||
bitmapData32[entry.bmWidth * y + x] = (color << 24) | 0xFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
|
||||
_dbg_assert_(!fullColor);
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
|
||||
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
|
||||
for (int y = 0; y < entry.bmHeight; y++) {
|
||||
for (int x = 0; x < entry.bmWidth; x++) {
|
||||
uint8_t bAlpha = (uint8_t)((bitmap[width * y + x] & 0xff) >> 4);
|
||||
uint8_t bAlpha = (uint8_t)((bitmap[bmWidth * y + x] & 0xff) >> 4);
|
||||
bitmapData16[entry.bmWidth * y + x] = (bAlpha) | 0xfff0;
|
||||
}
|
||||
}
|
||||
} else if (texFormat == Draw::DataFormat::A4R4G4B4_UNORM_PACK16) {
|
||||
_dbg_assert_(!fullColor);
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
|
||||
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
|
||||
for (int y = 0; y < entry.bmHeight; y++) {
|
||||
for (int x = 0; x < entry.bmWidth; x++) {
|
||||
uint8_t bAlpha = (uint8_t)((bitmap[width * y + x] & 0xff) >> 4);
|
||||
uint8_t bAlpha = (uint8_t)((bitmap[bmWidth * y + x] & 0xff) >> 4);
|
||||
bitmapData16[entry.bmWidth * y + x] = (bAlpha << 12) | 0x0fff;
|
||||
}
|
||||
}
|
||||
} else if (texFormat == Draw::DataFormat::R8_UNORM) {
|
||||
_dbg_assert_(!fullColor);
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight);
|
||||
for (int y = 0; y < entry.bmHeight; y++) {
|
||||
for (int x = 0; x < entry.bmWidth; x++) {
|
||||
uint8_t bAlpha = bitmap[width * y + x] & 0xff;
|
||||
uint8_t bAlpha = bitmap[bmWidth * y + x] & 0xff;
|
||||
bitmapData[entry.bmWidth * y + x] = bAlpha;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
#include <QtGui/QFontMetrics>
|
||||
#include <QtOpenGL/QGLWidget>
|
||||
|
||||
TextDrawerQt::TextDrawerQt(Draw::DrawContext *draw) : TextDrawer(draw) {
|
||||
}
|
||||
TextDrawerQt::TextDrawerQt(Draw::DrawContext *draw) : TextDrawer(draw) {}
|
||||
|
||||
TextDrawerQt::~TextDrawerQt() {
|
||||
ClearCache();
|
||||
|
@ -89,7 +88,9 @@ void TextDrawerQt::MeasureStringRect(std::string_view str, const Bounds &bounds,
|
|||
*h = (float)size.height() * fontScaleY_ * dpiScale_;
|
||||
}
|
||||
|
||||
bool TextDrawerQt::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
|
||||
bool TextDrawerQt::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
_dbg_assert_(!fullColor);
|
||||
|
||||
if (str.empty()) {
|
||||
bitmapData.clear();
|
||||
return false;
|
||||
|
@ -139,55 +140,6 @@ bool TextDrawerQt::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextString
|
|||
return true;
|
||||
}
|
||||
|
||||
void TextDrawerQt::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {
|
||||
using namespace Draw;
|
||||
if (str.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
|
||||
auto iter = cache_.find(key);
|
||||
if (iter != cache_.end()) {
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
} else {
|
||||
DataFormat texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
|
||||
|
||||
entry = new TextStringEntry(frameCount_);
|
||||
|
||||
TextureDesc desc{};
|
||||
std::vector<uint8_t> bitmapData;
|
||||
DrawStringBitmap(bitmapData, *entry, texFormat, str, align);
|
||||
desc.initData.push_back(&bitmapData[0]);
|
||||
|
||||
desc.type = TextureType::LINEAR2D;
|
||||
desc.format = texFormat;
|
||||
desc.width = entry->bmWidth;
|
||||
desc.height = entry->bmHeight;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.tag = "TextDrawer";
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
}
|
||||
|
||||
if (entry->texture) {
|
||||
draw_->BindTexture(0, entry->texture);
|
||||
}
|
||||
|
||||
float w = entry->bmWidth * fontScaleX_ * dpiScale_;
|
||||
float h = entry->bmHeight * fontScaleY_ * dpiScale_;
|
||||
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
|
||||
if (entry->texture) {
|
||||
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color);
|
||||
target.Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDrawerQt::ClearCache() {
|
||||
for (auto &iter : cache_) {
|
||||
if (iter.second->texture)
|
||||
|
|
|
@ -18,15 +18,15 @@ public:
|
|||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void MeasureString(std::string_view str, float *w, float *h) override;
|
||||
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
|
||||
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
// Use for housekeeping like throwing out old strings.
|
||||
void OncePerFrame() override;
|
||||
|
||||
protected:
|
||||
bool SupportsColorEmoji() const override { return false; }
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
uint32_t fontHash_;
|
||||
std::map<uint32_t, QFont *> fontMap_;
|
||||
};
|
||||
|
||||
|
|
|
@ -364,59 +364,9 @@ void TextDrawerSDL::MeasureStringRect(std::string_view str, const Bounds &bounds
|
|||
*h = total_h * fontScaleY_ * dpiScale_;
|
||||
}
|
||||
|
||||
void TextDrawerSDL::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {
|
||||
using namespace Draw;
|
||||
if (str.empty())
|
||||
return;
|
||||
bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
_dbg_assert_(!fullColor)
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
|
||||
auto iter = cache_.find(key);
|
||||
if (iter != cache_.end()) {
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
} else {
|
||||
DataFormat texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
|
||||
if ((draw_->GetDataFormatSupport(texFormat) & Draw::FMT_TEXTURE) == 0) {
|
||||
// This is always supported in Vulkan. The other format is the common OpenGL one.
|
||||
texFormat = Draw::DataFormat::B4G4R4A4_UNORM_PACK16;
|
||||
}
|
||||
|
||||
entry = new TextStringEntry(frameCount_);
|
||||
|
||||
TextureDesc desc{};
|
||||
std::vector<uint8_t> bitmapData;
|
||||
DrawStringBitmap(bitmapData, *entry, texFormat, str, align);
|
||||
desc.initData.push_back(&bitmapData[0]);
|
||||
|
||||
desc.type = TextureType::LINEAR2D;
|
||||
desc.format = texFormat;
|
||||
desc.width = entry->bmWidth;
|
||||
desc.height = entry->bmHeight;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.tag = "TextDrawer";
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
}
|
||||
|
||||
if (entry->texture) {
|
||||
draw_->BindTexture(0, entry->texture);
|
||||
}
|
||||
|
||||
float w = entry->bmWidth * fontScaleX_ * dpiScale_;
|
||||
float h = entry->bmHeight * fontScaleY_ * dpiScale_;
|
||||
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
|
||||
if (entry->texture) {
|
||||
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, 1.0f, 1.0f, color);
|
||||
target.Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool TextDrawerSDL::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
|
||||
if (str.empty()) {
|
||||
bitmapData.clear();
|
||||
return false;
|
||||
|
|
|
@ -21,18 +21,18 @@ public:
|
|||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void MeasureString(std::string_view str, float *w, float *h) override;
|
||||
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
|
||||
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
// Use for housekeeping like throwing out old strings.
|
||||
void OncePerFrame() override;
|
||||
|
||||
protected:
|
||||
bool SupportsColorEmoji() const override { return false; }
|
||||
|
||||
void ClearCache() override;
|
||||
void PrepareFallbackFonts(std::string_view locale);
|
||||
uint32_t CheckMissingGlyph(const std::string& text);
|
||||
int FindFallbackFonts(uint32_t missingGlyph, int ptSize);
|
||||
|
||||
uint32_t fontHash_;
|
||||
std::map<uint32_t, _TTF_Font *> fontMap_;
|
||||
|
||||
std::vector<_TTF_Font *> fallbackFonts_;
|
||||
|
|
|
@ -312,8 +312,8 @@ void TextDrawerUWP::MeasureStringRect(std::string_view str, const Bounds &bounds
|
|||
*h = total_h * fontScaleY_ * dpiScale_;
|
||||
}
|
||||
|
||||
bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
|
||||
if (!str.empty()) {
|
||||
bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
if (str.empty()) {
|
||||
bitmapData.clear();
|
||||
return false;
|
||||
}
|
||||
|
@ -401,12 +401,17 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
|||
uint32_t *bmpLine = (uint32_t *)&map.bits[map.pitch * y];
|
||||
for (int x = 0; x < entry.bmWidth; x++) {
|
||||
uint32_t v = bmpLine[x];
|
||||
if (swap)
|
||||
v = (v & 0xFF00FF00) | ((v >> 16) & 0xFF) | ((v << 16) & 0xFF0000);
|
||||
bitmapData32[entry.bmWidth * y + x] = v;
|
||||
if (fullColor) {
|
||||
if (swap)
|
||||
v = (v & 0xFF00FF00) | ((v >> 16) & 0xFF) | ((v << 16) & 0xFF0000);
|
||||
bitmapData32[entry.bmWidth * y + x] = v;
|
||||
} else {
|
||||
bitmapData32[entry.bmWidth * y + x] = (v << 24) | 0xFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (texFormat == Draw::DataFormat::B4G4R4A4_UNORM_PACK16 || texFormat == Draw::DataFormat::R4G4B4A4_UNORM_PACK16) {
|
||||
_dbg_assert_(!fullColor);
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
|
||||
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
|
||||
for (int y = 0; y < entry.bmHeight; y++) {
|
||||
|
@ -416,6 +421,7 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
|||
}
|
||||
}
|
||||
} else if (texFormat == Draw::DataFormat::A4R4G4B4_UNORM_PACK16) {
|
||||
_dbg_assert_(!fullColor);
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight * sizeof(uint16_t));
|
||||
uint16_t *bitmapData16 = (uint16_t *)&bitmapData[0];
|
||||
for (int y = 0; y < entry.bmHeight; y++) {
|
||||
|
@ -425,6 +431,7 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
|||
}
|
||||
}
|
||||
} else if (texFormat == Draw::DataFormat::R8_UNORM) {
|
||||
_dbg_assert_(!fullColor);
|
||||
bitmapData.resize(entry.bmWidth * entry.bmHeight);
|
||||
for (int y = 0; y < entry.bmHeight; y++) {
|
||||
for (int x = 0; x < entry.bmWidth; x++) {
|
||||
|
@ -440,72 +447,6 @@ bool TextDrawerUWP::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStrin
|
|||
return true;
|
||||
}
|
||||
|
||||
void TextDrawerUWP::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {
|
||||
using namespace Draw;
|
||||
if (str.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
|
||||
auto iter = cache_.find(key);
|
||||
if (iter != cache_.end()) {
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
} else {
|
||||
DataFormat texFormat;
|
||||
|
||||
// For our purposes these are equivalent, so just choose the supported one. D3D can emulate them.
|
||||
if (draw_->GetDataFormatSupport(Draw::DataFormat::A4R4G4B4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::A4R4G4B4_UNORM_PACK16;
|
||||
else if (draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::B4G4R4A4_UNORM_PACK16;
|
||||
else
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
|
||||
bool emoji = AnyEmojiInString(key.text.c_str(), key.text.size());
|
||||
if (emoji)
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
|
||||
entry = new TextStringEntry(frameCount_);
|
||||
|
||||
// Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format
|
||||
// because we need white. Well, we could using swizzle, but not all our backends support that.
|
||||
TextureDesc desc{};
|
||||
std::vector<uint8_t> bitmapData;
|
||||
DrawStringBitmap(bitmapData, *entry, texFormat, str, align);
|
||||
desc.initData.push_back(&bitmapData[0]);
|
||||
|
||||
desc.type = TextureType::LINEAR2D;
|
||||
desc.format = texFormat;
|
||||
desc.width = entry->bmWidth;
|
||||
desc.height = entry->bmHeight;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.tag = "TextDrawer";
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
}
|
||||
|
||||
if (entry->texture) {
|
||||
draw_->BindTexture(0, entry->texture);
|
||||
}
|
||||
|
||||
// Okay, the texture is bound, let's draw.
|
||||
float w = entry->width * fontScaleX_ * dpiScale_;
|
||||
float h = entry->height * fontScaleY_ * dpiScale_;
|
||||
float u = entry->width / (float)entry->bmWidth;
|
||||
float v = entry->height / (float)entry->bmHeight;
|
||||
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
|
||||
if (entry->texture) {
|
||||
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, u, v, color);
|
||||
target.Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDrawerUWP::RecreateFonts() {
|
||||
for (auto &iter : fontMap_) {
|
||||
iter.second->dpiScale = dpiScale_;
|
||||
|
|
|
@ -23,20 +23,19 @@ public:
|
|||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void MeasureString(std::string_view str, float *w, float *h) override;
|
||||
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
|
||||
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
// Use for housekeeping like throwing out old strings.
|
||||
void OncePerFrame() override;
|
||||
|
||||
protected:
|
||||
bool SupportsColorEmoji() const override { return true; }
|
||||
|
||||
void ClearCache() override;
|
||||
void RecreateFonts(); // On DPI change
|
||||
|
||||
TextDrawerContext *ctx_;
|
||||
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
|
||||
uint32_t fontHash_;
|
||||
|
||||
// Direct2D drawing components.
|
||||
ID2D1Factory5* m_d2dFactory;
|
||||
ID2D1Device4* m_d2dDevice;
|
||||
|
|
|
@ -211,14 +211,13 @@ void TextDrawerWin32::MeasureStringRect(std::string_view str, const Bounds &boun
|
|||
*h = total_h * fontScaleY_ * dpiScale_;
|
||||
}
|
||||
|
||||
bool TextDrawerWin32::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align) {
|
||||
bool TextDrawerWin32::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) {
|
||||
if (str.empty()) {
|
||||
bitmapData.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring wstr = ConvertUTF8ToWString(ReplaceAll(str, "\n", "\r\n"));
|
||||
SIZE size;
|
||||
|
||||
auto iter = fontMap_.find(fontHash_);
|
||||
if (iter != fontMap_.end()) {
|
||||
|
@ -234,6 +233,7 @@ bool TextDrawerWin32::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
|||
|
||||
RECT textRect = { 0 };
|
||||
DrawTextExW(ctx_->hDC, (LPWSTR)wstr.c_str(), (int)wstr.size(), &textRect, DT_NOPREFIX | DT_TOP | dtAlign | DT_CALCRECT, 0);
|
||||
SIZE size;
|
||||
size.cx = textRect.right;
|
||||
size.cy = textRect.bottom;
|
||||
|
||||
|
@ -303,73 +303,6 @@ bool TextDrawerWin32::DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStr
|
|||
return true;
|
||||
}
|
||||
|
||||
void TextDrawerWin32::DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align) {
|
||||
using namespace Draw;
|
||||
if (str.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CacheKey key{ std::string(str), fontHash_ };
|
||||
target.Flush(true);
|
||||
|
||||
TextStringEntry *entry;
|
||||
|
||||
auto iter = cache_.find(key);
|
||||
if (iter != cache_.end()) {
|
||||
entry = iter->second.get();
|
||||
entry->lastUsedFrame = frameCount_;
|
||||
} else {
|
||||
DataFormat texFormat;
|
||||
// For our purposes these are equivalent, so just choose the supported one. D3D can emulate them.
|
||||
if (draw_->GetDataFormatSupport(Draw::DataFormat::A4R4G4B4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::A4R4G4B4_UNORM_PACK16;
|
||||
else if (draw_->GetDataFormatSupport(Draw::DataFormat::R4G4B4A4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::R4G4B4A4_UNORM_PACK16;
|
||||
else if (draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16) & FMT_TEXTURE)
|
||||
texFormat = Draw::DataFormat::B4G4R4A4_UNORM_PACK16;
|
||||
else
|
||||
texFormat = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
|
||||
entry = new TextStringEntry(frameCount_);
|
||||
|
||||
// Convert the bitmap to a Thin3D compatible array of 16-bit pixels. Can't use a single channel format
|
||||
// because we need white. Well, we could using swizzle, but not all our backends support that.
|
||||
TextureDesc desc{};
|
||||
std::vector<uint8_t> bitmapData;
|
||||
if (!DrawStringBitmap(bitmapData, *entry, texFormat, str, align)) {
|
||||
// Nothing drawn. Store that fact in the cache.
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
desc.initData.push_back(&bitmapData[0]);
|
||||
|
||||
desc.type = TextureType::LINEAR2D;
|
||||
desc.format = texFormat;
|
||||
desc.width = entry->bmWidth;
|
||||
desc.height = entry->bmHeight;
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.tag = "TextDrawer";
|
||||
entry->texture = draw_->CreateTexture(desc);
|
||||
cache_[key] = std::unique_ptr<TextStringEntry>(entry);
|
||||
}
|
||||
|
||||
if (entry->texture) {
|
||||
draw_->BindTexture(0, entry->texture);
|
||||
|
||||
// Okay, the texture is bound, let's draw.
|
||||
float w = entry->width * fontScaleX_ * dpiScale_;
|
||||
float h = entry->height * fontScaleY_ * dpiScale_;
|
||||
float u = entry->width / (float)entry->bmWidth;
|
||||
float v = entry->height / (float)entry->bmHeight;
|
||||
DrawBuffer::DoAlign(align, &x, &y, &w, &h);
|
||||
|
||||
target.DrawTexRect(x, y, x + w, y + h, 0.0f, 0.0f, u, v, color);
|
||||
target.Flush(true);
|
||||
}
|
||||
}
|
||||
|
||||
void TextDrawerWin32::RecreateFonts() {
|
||||
for (auto &iter : fontMap_) {
|
||||
iter.second->dpiScale = dpiScale_;
|
||||
|
|
|
@ -23,19 +23,18 @@ public:
|
|||
void SetFont(uint32_t fontHandle) override; // Shortcut once you've set the font once.
|
||||
void MeasureString(std::string_view str, float *w, float *h) override;
|
||||
void MeasureStringRect(std::string_view str, const Bounds &bounds, float *w, float *h, int align = ALIGN_TOPLEFT) override;
|
||||
void DrawString(DrawBuffer &target, std::string_view str, float x, float y, uint32_t color, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align = ALIGN_TOPLEFT) override;
|
||||
bool DrawStringBitmap(std::vector<uint8_t> &bitmapData, TextStringEntry &entry, Draw::DataFormat texFormat, std::string_view str, int align, bool fullColor) override;
|
||||
// Use for housekeeping like throwing out old strings.
|
||||
void OncePerFrame() override;
|
||||
|
||||
protected:
|
||||
bool SupportsColorEmoji() const override { return false; }
|
||||
|
||||
void ClearCache() override;
|
||||
void RecreateFonts(); // On DPI change
|
||||
|
||||
TextDrawerContext *ctx_;
|
||||
std::map<uint32_t, std::unique_ptr<TextDrawerFontContext>> fontMap_;
|
||||
|
||||
uint32_t fontHash_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -917,7 +917,7 @@ static PPGeTextDrawerImage PPGeGetTextImage(const char *text, const PPGeStyle &s
|
|||
textDrawer->SetFontScale(style.scale, style.scale);
|
||||
Bounds b(0, 0, maxWidth, 272.0f);
|
||||
std::string cleaned = ReplaceAll(text, "\r", "");
|
||||
textDrawer->DrawStringBitmapRect(bitmapData, im.entry, Draw::DataFormat::R8_UNORM, cleaned.c_str(), b, tdalign);
|
||||
textDrawer->DrawStringBitmapRect(bitmapData, im.entry, Draw::DataFormat::R8_UNORM, cleaned.c_str(), b, tdalign, false);
|
||||
|
||||
int bufwBytes = ((im.entry.bmWidth + 31) / 32) * 16;
|
||||
u32 sz = bufwBytes * (im.entry.bmHeight + 1);
|
||||
|
|
Loading…
Add table
Reference in a new issue