2017-02-26 15:40:53 +01:00
|
|
|
#include "ppsspp_config.h"
|
2019-03-11 16:42:41 +01:00
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
2020-10-04 10:10:55 +02:00
|
|
|
#include "Common/System/Display.h"
|
2020-10-04 10:30:18 +02:00
|
|
|
#include "Common/System/System.h"
|
2020-10-04 20:48:47 +02:00
|
|
|
#include "Common/UI/UI.h"
|
|
|
|
|
#include "Common/UI/View.h"
|
|
|
|
|
#include "Common/UI/Context.h"
|
2020-10-04 23:24:14 +02:00
|
|
|
#include "Common/Render/DrawBuffer.h"
|
2020-10-05 00:05:28 +02:00
|
|
|
#include "Common/Render/Text/draw_text.h"
|
2022-11-21 20:15:22 +01:00
|
|
|
#include "Common/Render/ManagedTexture.h"
|
2018-02-25 10:13:01 +01:00
|
|
|
#include "Common/Log.h"
|
2023-12-14 11:10:36 +01:00
|
|
|
#include "Common/TimeUtil.h"
|
2022-08-28 09:01:35 -07:00
|
|
|
#include "Common/LogReporting.h"
|
2018-02-25 10:13:01 +01:00
|
|
|
|
2017-06-04 10:57:46 +02:00
|
|
|
UIContext::UIContext() {
|
2013-08-30 14:44:23 +02:00
|
|
|
fontStyle_ = new UI::FontStyle();
|
2023-02-25 13:09:44 +01:00
|
|
|
bounds_ = Bounds(0, 0, g_display.dp_xres, g_display.dp_yres);
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIContext::~UIContext() {
|
2017-02-17 19:22:41 +01:00
|
|
|
sampler_->Release();
|
2013-08-30 14:44:23 +02:00
|
|
|
delete fontStyle_;
|
|
|
|
|
delete textDrawer_;
|
2023-12-13 12:21:12 +01:00
|
|
|
if (uitexture_)
|
|
|
|
|
uitexture_->Release();
|
|
|
|
|
if (fontTexture_)
|
|
|
|
|
fontTexture_->Release();
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
|
|
|
|
|
2023-12-08 12:29:24 +01:00
|
|
|
void UIContext::Init(Draw::DrawContext *thin3d, Draw::Pipeline *uipipe, Draw::Pipeline *uipipenotex, DrawBuffer *uidrawbuffer) {
|
2016-12-25 18:18:19 +01:00
|
|
|
using namespace Draw;
|
2017-01-30 14:33:38 +01:00
|
|
|
draw_ = thin3d;
|
2021-10-08 22:20:57 +02:00
|
|
|
sampler_ = draw_->CreateSamplerState({ TextureFilter::LINEAR, TextureFilter::LINEAR, TextureFilter::LINEAR, 0.0, TextureAddressMode::CLAMP_TO_EDGE, TextureAddressMode::CLAMP_TO_EDGE, TextureAddressMode::CLAMP_TO_EDGE, });
|
2016-12-26 11:06:17 +01:00
|
|
|
ui_pipeline_ = uipipe;
|
|
|
|
|
ui_pipeline_notex_ = uipipenotex;
|
2013-08-30 14:44:23 +02:00
|
|
|
uidrawbuffer_ = uidrawbuffer;
|
2017-06-04 10:57:46 +02:00
|
|
|
textDrawer_ = TextDrawer::Create(thin3d); // May return nullptr if no implementation is available for this platform.
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-31 17:03:34 +02:00
|
|
|
void UIContext::setUIAtlas(const std::string &name) {
|
2022-04-24 14:12:15 +02:00
|
|
|
_dbg_assert_(!name.empty());
|
2022-03-31 17:03:34 +02:00
|
|
|
UIAtlas_ = name;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-25 10:13:01 +01:00
|
|
|
void UIContext::BeginFrame() {
|
2023-12-14 11:10:36 +01:00
|
|
|
frameStartTime_ = time_now_d();
|
2022-03-31 17:03:34 +02:00
|
|
|
if (!uitexture_ || UIAtlas_ != lastUIAtlas_) {
|
2023-12-12 23:10:46 +01:00
|
|
|
uitexture_ = CreateTextureFromFile(draw_, UIAtlas_.c_str(), ImageFileType::ZIM, false);
|
2022-03-31 17:03:34 +02:00
|
|
|
lastUIAtlas_ = UIAtlas_;
|
2021-10-16 16:47:24 -07:00
|
|
|
if (!fontTexture_) {
|
2021-12-10 22:43:04 +01:00
|
|
|
#if PPSSPP_PLATFORM(WINDOWS) || PPSSPP_PLATFORM(ANDROID)
|
|
|
|
|
// Don't bother with loading font_atlas.zim
|
|
|
|
|
#else
|
2023-12-12 23:10:46 +01:00
|
|
|
fontTexture_ = CreateTextureFromFile(draw_, "font_atlas.zim", ImageFileType::ZIM, false);
|
2021-12-10 22:43:04 +01:00
|
|
|
#endif
|
|
|
|
|
if (!fontTexture_) {
|
|
|
|
|
// Load the smaller ascii font only, like on Android. For debug ui etc.
|
2023-12-12 23:10:46 +01:00
|
|
|
fontTexture_ = CreateTextureFromFile(draw_, "asciifont_atlas.zim", ImageFileType::ZIM, false);
|
2021-12-10 22:43:04 +01:00
|
|
|
if (!fontTexture_) {
|
2024-07-14 14:42:59 +02:00
|
|
|
WARN_LOG(Log::System, "Failed to load font_atlas.zim or asciifont_atlas.zim");
|
2021-12-10 22:43:04 +01:00
|
|
|
}
|
|
|
|
|
}
|
2021-10-16 16:47:24 -07:00
|
|
|
}
|
2018-02-25 10:13:01 +01:00
|
|
|
}
|
2018-12-18 23:56:36 +01:00
|
|
|
uidrawbuffer_->SetCurZ(0.0f);
|
2018-12-18 10:23:22 +01:00
|
|
|
ActivateTopScissor();
|
2016-12-27 22:26:49 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-27 15:18:35 -08:00
|
|
|
void UIContext::SetTintSaturation(float tint, float sat) {
|
|
|
|
|
uidrawbuffer_->SetTintSaturation(tint, sat);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-08 22:41:17 +02:00
|
|
|
void UIContext::Begin() {
|
2020-03-21 21:26:09 -07:00
|
|
|
BeginPipeline(ui_pipeline_, sampler_);
|
2012-11-20 23:35:40 +01:00
|
|
|
}
|
|
|
|
|
|
2013-06-08 22:41:17 +02:00
|
|
|
void UIContext::BeginNoTex() {
|
2017-01-30 14:33:38 +01:00
|
|
|
draw_->BindSamplerStates(0, 1, &sampler_);
|
2024-10-23 10:55:18 +02:00
|
|
|
ui_draw2d.Begin(ui_pipeline_notex_);
|
2013-03-30 19:23:02 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-16 21:48:35 +01:00
|
|
|
void UIContext::BeginPipeline(Draw::Pipeline *pipeline, Draw::SamplerState *samplerState) {
|
2021-01-17 01:08:48 +01:00
|
|
|
_assert_(pipeline != nullptr);
|
2021-09-11 18:01:36 -07:00
|
|
|
// Also clear out any other textures bound.
|
|
|
|
|
Draw::SamplerState *samplers[3]{ samplerState };
|
|
|
|
|
draw_->BindSamplerStates(0, 3, samplers);
|
|
|
|
|
Draw::Texture *textures[2]{};
|
|
|
|
|
draw_->BindTextures(1, 2, textures);
|
2020-03-21 21:26:09 -07:00
|
|
|
RebindTexture();
|
2024-10-23 10:55:18 +02:00
|
|
|
ui_draw2d.Begin(pipeline);
|
2018-12-16 21:48:35 +01:00
|
|
|
}
|
|
|
|
|
|
2013-06-08 22:41:17 +02:00
|
|
|
void UIContext::RebindTexture() const {
|
2024-01-19 13:44:49 +01:00
|
|
|
_dbg_assert_(uitexture_);
|
2020-03-21 21:26:09 -07:00
|
|
|
if (uitexture_)
|
2023-12-12 23:10:46 +01:00
|
|
|
draw_->BindTexture(0, uitexture_);
|
2012-11-21 15:30:43 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-16 16:47:24 -07:00
|
|
|
void UIContext::BindFontTexture() const {
|
|
|
|
|
// Fall back to the UI texture, in case they have an old atlas.
|
|
|
|
|
if (fontTexture_)
|
2023-12-12 23:10:46 +01:00
|
|
|
draw_->BindTexture(0, fontTexture_);
|
2021-10-16 16:47:24 -07:00
|
|
|
else if (uitexture_)
|
2023-12-12 23:10:46 +01:00
|
|
|
draw_->BindTexture(0, uitexture_);
|
2021-10-16 16:47:24 -07:00
|
|
|
}
|
|
|
|
|
|
2013-06-08 22:41:17 +02:00
|
|
|
void UIContext::Flush() {
|
|
|
|
|
if (uidrawbuffer_) {
|
2013-03-30 19:23:02 +01:00
|
|
|
uidrawbuffer_->Flush();
|
2012-11-20 23:35:40 +01:00
|
|
|
}
|
2012-11-21 15:30:43 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-16 21:48:35 +01:00
|
|
|
void UIContext::SetCurZ(float curZ) {
|
|
|
|
|
ui_draw2d.SetCurZ(curZ);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-19 14:28:24 -07:00
|
|
|
// TODO: Support transformed bounds using stencil instead.
|
2013-05-28 00:32:00 +02:00
|
|
|
void UIContext::PushScissor(const Bounds &bounds) {
|
|
|
|
|
Flush();
|
2017-03-19 14:28:24 -07:00
|
|
|
Bounds clipped = TransformBounds(bounds);
|
2013-08-18 22:29:50 +02:00
|
|
|
if (scissorStack_.size())
|
|
|
|
|
clipped.Clip(scissorStack_.back());
|
2017-12-10 14:20:32 -08:00
|
|
|
else
|
|
|
|
|
clipped.Clip(bounds_);
|
2013-08-18 22:29:50 +02:00
|
|
|
scissorStack_.push_back(clipped);
|
2013-05-28 00:32:00 +02:00
|
|
|
ActivateTopScissor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UIContext::PopScissor() {
|
|
|
|
|
Flush();
|
|
|
|
|
scissorStack_.pop_back();
|
|
|
|
|
ActivateTopScissor();
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 22:05:58 +02:00
|
|
|
Bounds UIContext::GetScissorBounds() {
|
|
|
|
|
if (!scissorStack_.empty())
|
|
|
|
|
return scissorStack_.back();
|
|
|
|
|
else
|
2014-02-10 12:35:36 +01:00
|
|
|
return bounds_;
|
2013-06-10 22:05:58 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-31 00:47:01 +02:00
|
|
|
Bounds UIContext::GetLayoutBounds() const {
|
|
|
|
|
Bounds bounds = GetBounds();
|
|
|
|
|
|
|
|
|
|
float left = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT);
|
|
|
|
|
float right = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT);
|
|
|
|
|
float top = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP);
|
|
|
|
|
float bottom = System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM);
|
|
|
|
|
|
|
|
|
|
// ILOG("Insets: %f %f %f %f", left, right, top, bottom);
|
|
|
|
|
|
|
|
|
|
// Adjust left edge to compensate for cutouts (notches) if any.
|
|
|
|
|
bounds.x += left;
|
|
|
|
|
bounds.w -= (left + right);
|
|
|
|
|
bounds.y += top;
|
|
|
|
|
bounds.h -= (top + bottom);
|
|
|
|
|
|
|
|
|
|
return bounds;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-28 00:32:00 +02:00
|
|
|
void UIContext::ActivateTopScissor() {
|
2017-01-17 20:26:19 +07:00
|
|
|
Bounds bounds;
|
2013-05-28 00:32:00 +02:00
|
|
|
if (scissorStack_.size()) {
|
2023-02-25 13:09:44 +01:00
|
|
|
float scale_x = g_display.pixel_in_dps_x;
|
|
|
|
|
float scale_y = g_display.pixel_in_dps_y;
|
2017-01-17 20:26:19 +07:00
|
|
|
bounds = scissorStack_.back();
|
2017-08-07 13:18:19 +02:00
|
|
|
int x = floorf(scale_x * bounds.x);
|
|
|
|
|
int y = floorf(scale_y * bounds.y);
|
2019-03-11 16:42:41 +01:00
|
|
|
int w = std::max(0.0f, ceilf(scale_x * bounds.w));
|
|
|
|
|
int h = std::max(0.0f, ceilf(scale_y * bounds.h));
|
2023-02-25 13:09:44 +01:00
|
|
|
if (x < 0 || y < 0 || x + w > g_display.pixel_xres || y + h > g_display.pixel_yres) {
|
2024-07-14 14:42:59 +02:00
|
|
|
DEBUG_LOG(Log::G3D, "UI scissor out of bounds: %d,%d-%d,%d / %d,%d", x, y, w, h, g_display.pixel_xres, g_display.pixel_yres);
|
2023-01-24 16:48:13 +01:00
|
|
|
if (x < 0) { w += x; x = 0; }
|
|
|
|
|
if (y < 0) { h += y; y = 0; }
|
2023-02-25 13:09:44 +01:00
|
|
|
if (x >= g_display.pixel_xres) { x = g_display.pixel_xres - 1; }
|
|
|
|
|
if (y >= g_display.pixel_yres) { y = g_display.pixel_yres - 1; }
|
|
|
|
|
if (x + w > g_display.pixel_xres) { w = std::min(w, g_display.pixel_xres - x); }
|
|
|
|
|
if (y + w > g_display.pixel_yres) { h = std::min(h, g_display.pixel_yres - y); }
|
2023-01-24 16:48:13 +01:00
|
|
|
if (w == 0) w = 1;
|
|
|
|
|
if (h == 0) h = 1;
|
|
|
|
|
draw_->SetScissorRect(x, y, w, h);
|
|
|
|
|
} else {
|
|
|
|
|
// Avoid invalid rects
|
|
|
|
|
if (w == 0) w = 1;
|
|
|
|
|
if (h == 0) h = 1;
|
|
|
|
|
draw_->SetScissorRect(x, y, w, h);
|
2022-08-28 09:01:35 -07:00
|
|
|
}
|
2017-03-19 14:28:24 -07:00
|
|
|
} else {
|
2017-03-06 10:36:31 +01:00
|
|
|
// Avoid rounding errors
|
2023-02-25 13:09:44 +01:00
|
|
|
draw_->SetScissorRect(0, 0, g_display.pixel_xres, g_display.pixel_yres);
|
2013-05-28 00:32:00 +02:00
|
|
|
}
|
2013-06-08 22:41:17 +02:00
|
|
|
}
|
|
|
|
|
|
2013-08-30 14:44:23 +02:00
|
|
|
void UIContext::SetFontScale(float scaleX, float scaleY) {
|
|
|
|
|
fontScaleX_ = scaleX;
|
|
|
|
|
fontScaleY_ = scaleY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UIContext::SetFontStyle(const UI::FontStyle &fontStyle) {
|
|
|
|
|
*fontStyle_ = fontStyle;
|
|
|
|
|
if (textDrawer_) {
|
|
|
|
|
textDrawer_->SetFontScale(fontScaleX_, fontScaleY_);
|
2016-03-17 19:42:59 +01:00
|
|
|
textDrawer_->SetFont(fontStyle.fontName.c_str(), fontStyle.sizePts, fontStyle.flags);
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 12:21:08 +02:00
|
|
|
void UIContext::MeasureText(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, float *x, float *y, int align) const {
|
|
|
|
|
_dbg_assert_(str.data() != nullptr);
|
2013-08-30 14:44:23 +02:00
|
|
|
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
|
|
|
|
float sizeFactor = (float)style.sizePts / 24.0f;
|
2016-08-07 16:49:50 -07:00
|
|
|
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
|
2024-05-24 12:29:10 +02:00
|
|
|
Draw()->MeasureText(style.atlasFont, str, x, y);
|
2013-08-30 14:44:23 +02:00
|
|
|
} else {
|
2016-08-07 12:43:46 -07:00
|
|
|
textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags);
|
2016-08-07 16:49:50 -07:00
|
|
|
textDrawer_->SetFontScale(scaleX, scaleY);
|
2024-05-24 13:56:10 +02:00
|
|
|
textDrawer_->MeasureString(str, x, y);
|
2016-08-07 12:43:46 -07:00
|
|
|
textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags);
|
2016-07-04 11:46:21 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 12:21:08 +02:00
|
|
|
void UIContext::MeasureTextRect(const UI::FontStyle &style, float scaleX, float scaleY, std::string_view str, const Bounds &bounds, float *x, float *y, int align) const {
|
|
|
|
|
_dbg_assert_(str.data() != nullptr);
|
2016-07-04 11:46:21 -07:00
|
|
|
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
|
|
|
|
float sizeFactor = (float)style.sizePts / 24.0f;
|
2016-08-07 16:49:50 -07:00
|
|
|
Draw()->SetFontScale(scaleX * sizeFactor, scaleY * sizeFactor);
|
2024-05-24 12:29:10 +02:00
|
|
|
Draw()->MeasureTextRect(style.atlasFont, str, bounds, x, y, align);
|
2016-07-04 11:46:21 -07:00
|
|
|
} else {
|
2016-08-07 12:43:46 -07:00
|
|
|
textDrawer_->SetFont(style.fontName.c_str(), style.sizePts, style.flags);
|
2016-08-07 16:49:50 -07:00
|
|
|
textDrawer_->SetFontScale(scaleX, scaleY);
|
2024-05-24 13:56:10 +02:00
|
|
|
textDrawer_->MeasureStringRect(str, bounds, x, y, align);
|
2016-08-07 12:43:46 -07:00
|
|
|
textDrawer_->SetFont(fontStyle_->fontName.c_str(), fontStyle_->sizePts, fontStyle_->flags);
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 14:25:10 +02:00
|
|
|
void UIContext::DrawText(std::string_view str, float x, float y, uint32_t color, int align) {
|
|
|
|
|
_dbg_assert_(str.data() != nullptr);
|
2013-08-30 14:44:23 +02:00
|
|
|
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
2021-10-16 16:47:24 -07:00
|
|
|
// Use the font texture if this font is in that texture instead.
|
|
|
|
|
bool useFontTexture = Draw()->GetFontAtlas()->getFont(fontStyle_->atlasFont) != nullptr;
|
|
|
|
|
if (useFontTexture) {
|
|
|
|
|
Flush();
|
|
|
|
|
BindFontTexture();
|
|
|
|
|
}
|
2013-08-30 14:44:23 +02:00
|
|
|
float sizeFactor = (float)fontStyle_->sizePts / 24.0f;
|
|
|
|
|
Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor);
|
|
|
|
|
Draw()->DrawText(fontStyle_->atlasFont, str, x, y, color, align);
|
2021-10-16 16:47:24 -07:00
|
|
|
if (useFontTexture)
|
|
|
|
|
Flush();
|
2013-08-30 14:44:23 +02:00
|
|
|
} else {
|
|
|
|
|
textDrawer_->SetFontScale(fontScaleX_, fontScaleY_);
|
|
|
|
|
textDrawer_->DrawString(*Draw(), str, x, y, color, align);
|
|
|
|
|
}
|
2021-10-16 16:47:24 -07:00
|
|
|
RebindTexture();
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
|
|
|
|
|
2024-05-24 14:25:10 +02:00
|
|
|
void UIContext::DrawTextShadow(std::string_view str, float x, float y, uint32_t color, int align) {
|
2015-05-07 10:45:24 +08:00
|
|
|
uint32_t alpha = (color >> 1) & 0xFF000000;
|
|
|
|
|
DrawText(str, x + 2, y + 2, alpha, align);
|
|
|
|
|
DrawText(str, x, y, color, align);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 14:25:10 +02:00
|
|
|
void UIContext::DrawTextRect(std::string_view str, const Bounds &bounds, uint32_t color, int align) {
|
2013-08-30 14:44:23 +02:00
|
|
|
if (!textDrawer_ || (align & FLAG_DYNAMIC_ASCII)) {
|
2021-10-16 16:47:24 -07:00
|
|
|
// Use the font texture if this font is in that texture instead.
|
|
|
|
|
bool useFontTexture = Draw()->GetFontAtlas()->getFont(fontStyle_->atlasFont) != nullptr;
|
|
|
|
|
if (useFontTexture) {
|
|
|
|
|
Flush();
|
|
|
|
|
BindFontTexture();
|
|
|
|
|
}
|
2013-08-31 01:00:57 -07:00
|
|
|
float sizeFactor = (float)fontStyle_->sizePts / 24.0f;
|
|
|
|
|
Draw()->SetFontScale(fontScaleX_ * sizeFactor, fontScaleY_ * sizeFactor);
|
2013-08-30 14:44:23 +02:00
|
|
|
Draw()->DrawTextRect(fontStyle_->atlasFont, str, bounds.x, bounds.y, bounds.w, bounds.h, color, align);
|
2021-10-16 16:47:24 -07:00
|
|
|
if (useFontTexture)
|
|
|
|
|
Flush();
|
2013-08-30 14:44:23 +02:00
|
|
|
} else {
|
|
|
|
|
textDrawer_->SetFontScale(fontScaleX_, fontScaleY_);
|
2017-11-23 15:07:59 +01:00
|
|
|
Bounds rounded = bounds;
|
|
|
|
|
rounded.x = floorf(rounded.x);
|
|
|
|
|
rounded.y = floorf(rounded.y);
|
|
|
|
|
textDrawer_->DrawStringRect(*Draw(), str, rounded, color, align);
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
2021-10-16 16:47:24 -07:00
|
|
|
RebindTexture();
|
2013-08-30 14:44:23 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-17 11:44:36 +02:00
|
|
|
static constexpr float MIN_TEXT_SCALE = 0.7f;
|
|
|
|
|
|
2024-05-24 12:21:08 +02:00
|
|
|
float UIContext::CalculateTextScale(std::string_view str, float availWidth, float availHeight) const {
|
2023-07-17 11:44:36 +02:00
|
|
|
float actualWidth, actualHeight;
|
|
|
|
|
Bounds availBounds(0, 0, availWidth, availHeight);
|
2024-05-24 12:21:08 +02:00
|
|
|
MeasureTextRect(theme->uiFont, 1.0f, 1.0f, str, availBounds, &actualWidth, &actualHeight, ALIGN_VCENTER);
|
2023-07-17 11:44:36 +02:00
|
|
|
if (actualWidth > availWidth) {
|
|
|
|
|
return std::max(MIN_TEXT_SCALE, availWidth / actualWidth);
|
|
|
|
|
}
|
|
|
|
|
return 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 14:25:10 +02:00
|
|
|
void UIContext::DrawTextRectSqueeze(std::string_view str, const Bounds &bounds, uint32_t color, int align) {
|
2024-11-21 14:53:45 +01:00
|
|
|
if (bounds.w <= 0 || bounds.h <= 0) {
|
|
|
|
|
// Probably mid-layout.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-07-17 11:44:36 +02:00
|
|
|
float origScaleX = fontScaleX_;
|
|
|
|
|
float origScaleY = fontScaleY_;
|
|
|
|
|
float scale = CalculateTextScale(str, bounds.w / origScaleX, bounds.h / origScaleY);
|
|
|
|
|
SetFontScale(scale * origScaleX, scale * origScaleY);
|
|
|
|
|
Bounds textBounds(bounds.x, bounds.y, bounds.w, bounds.h);
|
|
|
|
|
DrawTextRect(str, textBounds, color, align);
|
|
|
|
|
SetFontScale(origScaleX, origScaleY);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-24 14:25:10 +02:00
|
|
|
void UIContext::DrawTextShadowRect(std::string_view str, const Bounds &bounds, uint32_t color, int align) {
|
2021-09-24 11:13:01 +02:00
|
|
|
uint32_t alpha = (color >> 1) & 0xFF000000;
|
|
|
|
|
Bounds shadowBounds(bounds.x+2, bounds.y+2, bounds.w, bounds.h);
|
|
|
|
|
DrawTextRect(str, shadowBounds, alpha, align);
|
|
|
|
|
DrawTextRect(str, bounds, color, align);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-08 22:41:17 +02:00
|
|
|
void UIContext::FillRect(const UI::Drawable &drawable, const Bounds &bounds) {
|
2013-11-26 13:56:56 +01:00
|
|
|
// Only draw if alpha is non-zero.
|
|
|
|
|
if ((drawable.color & 0xFF000000) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-06-08 22:41:17 +02:00
|
|
|
switch (drawable.type) {
|
|
|
|
|
case UI::DRAW_SOLID_COLOR:
|
2021-01-31 15:49:52 +01:00
|
|
|
uidrawbuffer_->DrawImageCenterTexel(theme->whiteImage, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color);
|
2013-06-08 22:41:17 +02:00
|
|
|
break;
|
|
|
|
|
case UI::DRAW_4GRID:
|
|
|
|
|
uidrawbuffer_->DrawImage4Grid(drawable.image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color);
|
|
|
|
|
break;
|
|
|
|
|
case UI::DRAW_STRETCH_IMAGE:
|
|
|
|
|
uidrawbuffer_->DrawImageStretch(drawable.image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), drawable.color);
|
|
|
|
|
break;
|
2013-10-19 14:29:05 -07:00
|
|
|
case UI::DRAW_NOTHING:
|
|
|
|
|
break;
|
2013-06-08 22:41:17 +02:00
|
|
|
}
|
2013-10-19 14:29:05 -07:00
|
|
|
}
|
2017-03-19 14:28:24 -07:00
|
|
|
|
2023-07-03 22:54:25 +02:00
|
|
|
void UIContext::DrawRectDropShadow(const Bounds &bounds, float radius, float alpha, uint32_t color) {
|
|
|
|
|
if (alpha <= 0.0f)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
color = colorAlpha(color, alpha);
|
|
|
|
|
|
|
|
|
|
// Bias the shadow downwards a bit.
|
|
|
|
|
Bounds shadowBounds = bounds.Expand(radius, 0.5 * radius, radius, 1.5 * radius);
|
|
|
|
|
Draw()->DrawImage4Grid(theme->dropShadow4Grid, shadowBounds.x, shadowBounds.y, shadowBounds.x2(), shadowBounds.y2(), color, radius * (1.0f / 24.0f) * 2.0f);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-17 13:44:57 +01:00
|
|
|
void UIContext::DrawImageVGradient(ImageID image, uint32_t color1, uint32_t color2, const Bounds &bounds) {
|
|
|
|
|
uidrawbuffer_->DrawImageStretchVGradient(image, bounds.x, bounds.y, bounds.x2(), bounds.y2(), color1, color2);
|
2021-01-17 01:08:48 +01:00
|
|
|
}
|
|
|
|
|
|
2017-03-19 14:28:24 -07:00
|
|
|
void UIContext::PushTransform(const UITransform &transform) {
|
|
|
|
|
Flush();
|
|
|
|
|
|
2019-10-22 22:58:10 +02:00
|
|
|
using namespace Lin;
|
|
|
|
|
|
2017-03-19 14:28:24 -07:00
|
|
|
Matrix4x4 m = Draw()->GetDrawMatrix();
|
|
|
|
|
const Vec3 &t = transform.translate;
|
|
|
|
|
Vec3 scaledTranslate = Vec3(
|
|
|
|
|
t.x * m.xx + t.y * m.xy + t.z * m.xz + m.xw,
|
|
|
|
|
t.x * m.yx + t.y * m.yy + t.z * m.yz + m.yw,
|
|
|
|
|
t.x * m.zx + t.y * m.zy + t.z * m.zz + m.zw);
|
|
|
|
|
|
|
|
|
|
m.translateAndScale(scaledTranslate, transform.scale);
|
|
|
|
|
Draw()->PushDrawMatrix(m);
|
|
|
|
|
Draw()->PushAlpha(transform.alpha);
|
|
|
|
|
|
|
|
|
|
transformStack_.push_back(transform);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UIContext::PopTransform() {
|
|
|
|
|
Flush();
|
|
|
|
|
|
|
|
|
|
transformStack_.pop_back();
|
|
|
|
|
|
|
|
|
|
Draw()->PopDrawMatrix();
|
|
|
|
|
Draw()->PopAlpha();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Bounds UIContext::TransformBounds(const Bounds &bounds) {
|
|
|
|
|
if (!transformStack_.empty()) {
|
|
|
|
|
const UITransform t = transformStack_.back();
|
|
|
|
|
Bounds translated = bounds.Offset(t.translate.x, t.translate.y);
|
|
|
|
|
|
|
|
|
|
// Scale around the center as the origin.
|
2023-02-25 13:09:44 +01:00
|
|
|
float scaledX = (translated.x - g_display.dp_xres * 0.5f) * t.scale.x + g_display.dp_xres * 0.5f;
|
|
|
|
|
float scaledY = (translated.y - g_display.dp_yres * 0.5f) * t.scale.y + g_display.dp_yres * 0.5f;
|
2017-03-19 14:28:24 -07:00
|
|
|
|
|
|
|
|
return Bounds(scaledX, scaledY, translated.w * t.scale.x, translated.h * t.scale.y);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bounds;
|
|
|
|
|
}
|