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"
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_ ;
}
2016-12-27 22:26:49 +01:00
void UIContext : : Init ( Draw : : DrawContext * thin3d , Draw : : Pipeline * uipipe , Draw : : Pipeline * uipipenotex , DrawBuffer * uidrawbuffer , DrawBuffer * uidrawbufferTop ) {
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 ;
uidrawbufferTop_ = uidrawbufferTop ;
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 ( ) {
2022-03-31 17:03:34 +02:00
if ( ! uitexture_ | | UIAtlas_ ! = lastUIAtlas_ ) {
uitexture_ = CreateTextureFromFile ( draw_ , UIAtlas_ . c_str ( ) , ImageFileType : : ZIM , false ) ;
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
2021-10-16 16:47:24 -07: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.
fontTexture_ = CreateTextureFromFile ( draw_ , " asciifont_atlas.zim " , ImageFileType : : ZIM , false ) ;
if ( ! fontTexture_ ) {
2021-12-11 08:38:10 -08:00
WARN_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
uidrawbufferTop_ - > SetCurZ ( 0.0f ) ;
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 ) ;
uidrawbufferTop_ - > 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_ ) ;
2016-12-26 11:06:17 +01:00
UIBegin ( ui_pipeline_notex_ ) ;
2012-11-21 15:30:43 +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 ( ) ;
2018-12-16 21:48:35 +01:00
UIBegin ( pipeline ) ;
}
2013-06-08 22:41:17 +02:00
void UIContext : : RebindTexture ( ) const {
2020-03-21 21:26:09 -07:00
if ( uitexture_ )
draw_ - > BindTexture ( 0 , uitexture_ - > GetTexture ( ) ) ;
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_ )
draw_ - > BindTexture ( 0 , fontTexture_ - > GetTexture ( ) ) ;
else if ( uitexture_ )
draw_ - > BindTexture ( 0 , uitexture_ - > GetTexture ( ) ) ;
}
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-21 15:30:43 +01:00
}
2013-06-08 22:41:17 +02:00
if ( uidrawbufferTop_ ) {
2013-03-30 19:23:02 +01:00
uidrawbufferTop_ - > Flush ( ) ;
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 ) ;
ui_draw2d_front . 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 ) {
2022-08-28 09:01:35 -07:00
// This won't actually report outside a game, but we can try.
2023-02-25 13:09:44 +01:00
DEBUG_LOG ( G3D , " UI scissor out of bounds in %sScreen: %d,%d-%d,%d / %d,%d " , screenTag_ ? screenTag_ : " N/A " , 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
}
}
2016-08-07 16:49:50 -07:00
void UIContext : : MeasureText ( const UI : : FontStyle & style , float scaleX , float scaleY , const char * str , float * x , float * y , int align ) const {
2023-07-10 19:26:41 +02:00
_dbg_assert_ ( str ! = nullptr ) ;
2016-08-07 16:49:50 -07:00
MeasureTextCount ( style , scaleX , scaleY , str , ( int ) strlen ( str ) , x , y , align ) ;
2014-07-21 11:59:05 +02:00
}
2016-08-07 16:49:50 -07:00
void UIContext : : MeasureTextCount ( const UI : : FontStyle & style , float scaleX , float scaleY , const char * str , int count , float * x , float * y , int align ) const {
2023-07-10 19:26:41 +02:00
_dbg_assert_ ( str ! = 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 ) ;
2014-07-21 11:59:05 +02:00
Draw ( ) - > MeasureTextCount ( style . atlasFont , str , count , 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 ) ;
2016-07-04 11:46:21 -07:00
textDrawer_ - > MeasureString ( str , count , 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
}
}
2016-08-07 16:49:50 -07:00
void UIContext : : MeasureTextRect ( const UI : : FontStyle & style , float scaleX , float scaleY , const char * str , int count , const Bounds & bounds , float * x , float * y , int align ) const {
2023-07-10 19:26:41 +02:00
_dbg_assert_ ( str ! = 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 ) ;
2016-07-04 11:46:21 -07:00
Draw ( ) - > MeasureTextRect ( style . atlasFont , str , count , bounds , x , y , align ) ;
} 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 ) ;
2016-07-04 11:46:21 -07:00
textDrawer_ - > MeasureStringRect ( str , count , 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
}
}
void UIContext : : DrawText ( const char * str , float x , float y , uint32_t color , int align ) {
2023-07-10 19:26:41 +02:00
_dbg_assert_ ( str ! = 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
}
2015-05-07 10:45:24 +08:00
void UIContext : : DrawTextShadow ( const char * str , float x , float y , uint32_t color , int align ) {
uint32_t alpha = ( color > > 1 ) & 0xFF000000 ;
DrawText ( str , x + 2 , y + 2 , alpha , align ) ;
DrawText ( str , x , y , color , align ) ;
}
2013-08-30 14:44:23 +02:00
void UIContext : : DrawTextRect ( const char * str , const Bounds & bounds , uint32_t color , int align ) {
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 ;
float UIContext : : CalculateTextScale ( const char * text , float availWidth , float availHeight ) const {
float actualWidth , actualHeight ;
Bounds availBounds ( 0 , 0 , availWidth , availHeight ) ;
MeasureTextRect ( theme - > uiFont , 1.0f , 1.0f , text , ( int ) strlen ( text ) , availBounds , & actualWidth , & actualHeight , ALIGN_VCENTER ) ;
if ( actualWidth > availWidth ) {
return std : : max ( MIN_TEXT_SCALE , availWidth / actualWidth ) ;
}
return 1.0f ;
}
void UIContext : : DrawTextRectSqueeze ( const char * str , const Bounds & bounds , uint32_t color , int align ) {
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 ) ;
}
2021-09-24 11:13:01 +02:00
void UIContext : : DrawTextShadowRect ( const char * str , const Bounds & bounds , uint32_t color , int align ) {
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 ;
}