@ -1148,12 +1148,14 @@ namespace olc
void DrawPartialRotatedDecal ( const olc : : vf2d & pos , olc : : Decal * decal , const float fAngle , const olc : : vf2d & center , const olc : : vf2d & source_pos , const olc : : vf2d & source_size , const olc : : vf2d & scale = { 1.0f , 1.0f } , const olc : : Pixel & tint = olc : : WHITE ) ;
void DrawPartialSquishedRotatedDecal ( const olc : : vf2d & pos , olc : : Decal * decal , const float fAngle , const olc : : vf2d & center , const olc : : vf2d & source_pos , const olc : : vf2d & source_size , const olc : : vf2d & scale = { 1.0f , 1.0f } , const olc : : vf2d & squishScale = { 1.0f , 1.0f } , const olc : : Pixel & tint = olc : : WHITE ) ;
// Draws a multiline string as a decal, with tiniting and scaling
void DrawStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const bool disableDynamicScaling = false ) ;
void DrawStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) ) ;
void DrawOGStringDecal ( const olc : : vf2d & pos , const std : : string & sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } ) ;
void DrawStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } ) ;
void DrawStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const bool disableDynamicScaling = false ) ;
void DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const float shadowSizeFactor = 1 , const bool disableDynamicScaling = false ) ;
void DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) , const float shadowSizeFactor = 1 , const bool disableDynamicScaling = false ) ;
void DrawStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = olc : : WHITE , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float width = std : : numeric_limits < float > : : max ( ) ) ;
void DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = WHITE , const Pixel shadowCol = BLACK , const olc : : vf2d & scale = { 1.f , 1.f } ) ;
void DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const olc : : vf2d & shadowScale , const float width = std : : numeric_limits < float > : : max ( ) ) ;
void DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col = WHITE , const Pixel shadowCol = BLACK , const olc : : vf2d & scale = { 1.f , 1.f } ) ;
void DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const olc : : vf2d & shadowScale , const float width = std : : numeric_limits < float > : : max ( ) ) ;
void DrawShadowStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } , const float shadowSizeFactor = 1 ) ;
void DrawDropShadowStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col = olc : : WHITE , const Pixel shadowCol = olc : : BLACK , const olc : : vf2d & scale = { 1.0f , 1.0f } ) ;
// Draws a single shaded filled rectangle as a decal
@ -1182,7 +1184,7 @@ namespace olc
olc : : Sprite * GetFontSprite ( ) ;
// Returns the font image
olc : : Decal * GetFontDecal ( ) ;
void SetFontSprite ( std : : string_view filename , ResourcePack * pack , bool generateGamePack ) ;
void SetFontSprite ( std : : string_view filename , std : : string_view shadowFilename , ResourcePack * pack , bool generateGamePack ) ;
// Clip a line segment to visible area
bool ClipLineToScreen ( olc : : vi2d & in_p1 , olc : : vi2d & in_p2 ) ;
@ -1311,7 +1313,7 @@ namespace olc
float fLastElapsed = 0.0f ;
int nFrameCount = 0 ;
bool bSuspendTextureTransfer = false ;
Renderable fontRenderable ;
Renderable fontRenderable , fontRenderableShadow ;
std : : vector < LayerDesc > vLayers ;
uint8_t nTargetLayer = 0 ;
uint32_t nLastFPS = 0 ;
@ -2474,16 +2476,20 @@ namespace olc
{ return fontRenderable . Sprite ( ) ; }
olc : : Decal * PixelGameEngine : : GetFontDecal ( )
{ return fontRenderable . Decal ( ) ; }
void PixelGameEngine : : SetFontSprite ( std : : string_view filename , ResourcePack * pack , bool generateGamePack )
void PixelGameEngine : : SetFontSprite ( std : : string_view filename , std : : string_view shadowFilename , ResourcePack * pack , bool generateGamePack )
{
if ( pack - > Loaded ( ) ) {
fontRenderable . Load ( std : : string ( filename ) , pack ) ;
fontRenderableShadow . Load ( std : : string ( shadowFilename ) , pack ) ;
} else {
fontRenderable . Load ( std : : string ( filename ) , nullptr ) ;
fontRenderableShadow . Load ( std : : string ( shadowFilename ) , nullptr ) ;
if ( generateGamePack ) {
pack - > AddFile ( std : : string ( filename ) ) ;
pack - > AddFile ( std : : string ( shadowFilename ) ) ;
}
}
//Generate shadow version.
}
bool PixelGameEngine : : ClipLineToScreen ( olc : : vi2d & in_p1 , olc : : vi2d & in_p2 )
@ -3483,120 +3489,325 @@ namespace olc
}
}
void PixelGameEngine : : DrawStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const olc : : vf2d & scale , const float width , const bool disableDynamicScaling )
void PixelGameEngine : : DrawStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const olc : : vf2d & scale , const float width )
{
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { stripCol ( sText ) } ;
std : : string renderStr { stripLeadingCol ( sText ) } ;
std : : string key { " DSD " + std : : string ( originalKey ) } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = garbageCollector . count ( key ) & & garbageCollector [ key ] . originalStr ! = renderStr ;
if ( ! garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = GetWrappedTextSize ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = garbageCollector [ key ] . decal ;
Pixel textCol = col ;
static std : : vector < PixelGameEngine : : StringDecalData > letters ;
letters . clear ( ) ;
bool wrappingOccurred = false ;
olc : : vf2d planningMarker = { 0.0f , 0.0f } ;
olc : : vf2d drawingMarker = { 0.0f , 0.0f } ;
const auto hexToNumber = [ ] ( char c ) {
if ( c < = ' 9 ' ) return c - ' 0 ' ;
return ( c - ' A ' ) + 10 ;
} ;
for ( int skip = 0 , index = - 1 ; auto c : sText )
{
index + + ;
if ( skip ) {
skip - - ;
continue ;
}
if ( c = = ' ' | | c = = ' \t ' | | c = = ' \n ' ) {
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
letters . clear ( ) ;
wrappingOccurred = false ;
}
if ( wrappingOccurred ) {
if ( c ! = ' ' & & c ! = ' \t ' & & c ! = ' \n ' ) {
wrappingOccurred = false ;
} else {
continue ;
}
}
if ( c = = ' \r ' ) continue ; //Ignore carriage returns. Stupid Linux.
if ( c = = ' \n ' )
{
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
letters . clear ( ) ;
drawingMarker . x = 0 ; drawingMarker . y + = 8.0f * scale . y ;
}
else if ( c = = ' ' )
{
drawingMarker . x + = 8.0f * scale . x ;
planningMarker . x + = 8.0f * scale . x ;
}
else if ( c = = ' \t ' )
{
drawingMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
planningMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
}
else if ( c > = - 128 & & c < - 105 )
{
textCol = { PixelGameEngine : : charToColor [ c ] . r , PixelGameEngine : : charToColor [ c ] . g , PixelGameEngine : : charToColor [ c ] . b , col . a } ;
}
else if ( c = = PixelGameEngine : : Reset [ 0 ] )
{
textCol = col ;
}
else if ( c = = ' # ' )
{
skip = 6 ;
textCol = BLACK ;
for ( int i = 1 ; i < 7 ; i + + ) {
if ( i < 3 ) {
textCol . r * = 16 ;
textCol . r + = hexToNumber ( sText [ index + i ] ) ;
} else
if ( i < 5 ) {
textCol . g * = 16 ;
textCol . g + = hexToNumber ( sText [ index + i ] ) ;
} else {
textCol . b * = 16 ;
textCol . b + = hexToNumber ( sText [ index + i ] ) ;
}
}
if ( textCol = = WHITE ) textCol = col ;
}
else
{
int32_t ox = ( c - 32 ) % 16 ;
int32_t oy = ( c - 32 ) / 16 ;
planningMarker . x + = 8.0f * scale . x ;
if ( planningMarker . x > width ) {
if ( drawingMarker . x = = 0 ) { //The text has overflowed a full line, so we have to dump the line.
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
letters . clear ( ) ;
} else {
drawingMarker . x - = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ; //Don't include the space in the sizing when wrapping.
}
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
drawingMarker = planningMarker ;
wrappingOccurred = true ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
planningMarker . x + = 8.0f * scale . x ;
}
}
letters . emplace_back ( c , vf2d { float ( ox ) * 8.0f , float ( oy ) * 8.0f } , vf2d { 8.0f , 8.0f } , textCol ) ;
}
garbageCollector [ key ] . originalStr = originalKey ;
SetDrawTarget ( newDecal - > sprite ) ;
Clear ( BLANK ) ;
DrawString ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
SetDrawTarget ( nullptr ) ;
newDecal - > Update ( ) ;
}
garbageCollector [ key ] . expireTime = GetRunTime ( ) + 120.0f ;
DrawDecal ( pos , garbageCollector [ key ] . decal , scale , GetFinalRenderColor ( col , sText ) ) ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
}
void PixelGameEngine : : DrawStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const olc : : vf2d & scale , const float width , const bool disableDynamicScaling )
void PixelGameEngine : : DrawStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const olc : : vf2d & scale , const float width )
{
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { stripCol ( sText ) } ;
std : : string renderStr { stripLeadingCol ( sText ) } ;
std : : string key { " DSPD_ " + std : : string ( originalKey ) } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = garbageCollector . count ( key ) & & garbageCollector [ key ] . originalStr ! = renderStr ;
if ( ! garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = GetWrappedTextSizeProp ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = garbageCollector [ key ] . decal ;
Pixel textCol = col ;
static std : : vector < PixelGameEngine : : StringDecalData > letters ;
letters . clear ( ) ;
bool wrappingOccurred = false ;
olc : : vf2d planningMarker = { 0.0f , 0.0f } ;
olc : : vf2d drawingMarker = { 0.0f , 0.0f } ;
const auto hexToNumber = [ ] ( char c ) {
if ( c < = ' 9 ' ) return c - ' 0 ' ;
return ( c - ' A ' ) + 10 ;
} ;
for ( int skip = 0 , index = - 1 ; auto c : sText )
{
index + + ;
if ( skip ) {
skip - - ;
continue ;
}
if ( c = = ' ' | | c = = ' \t ' | | c = = ' \n ' ) {
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
letters . clear ( ) ;
wrappingOccurred = false ;
}
if ( wrappingOccurred ) {
if ( c ! = ' ' & & c ! = ' \t ' & & c ! = ' \n ' ) {
wrappingOccurred = false ;
} else {
continue ;
}
}
if ( c = = ' \r ' ) continue ; //Ignore carriage returns. Stupid Linux.
if ( c = = ' \n ' )
{
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
letters . clear ( ) ;
drawingMarker . x = 0 ; drawingMarker . y + = 8.0f * scale . y ;
}
else if ( c = = ' ' )
{
drawingMarker . x + = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ;
planningMarker . x + = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ;
}
else if ( c = = ' \t ' )
{
drawingMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
planningMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
}
else if ( c > = - 128 & & c < - 105 )
{
textCol = { PixelGameEngine : : charToColor [ c ] . r , PixelGameEngine : : charToColor [ c ] . g , PixelGameEngine : : charToColor [ c ] . b , col . a } ;
}
else if ( c = = PixelGameEngine : : Reset [ 0 ] )
{
textCol = col ;
}
else if ( c = = ' # ' )
{
skip = 6 ;
textCol = BLACK ;
for ( int i = 1 ; i < 7 ; i + + ) {
if ( i < 3 ) {
textCol . r * = 16 ;
textCol . r + = hexToNumber ( sText [ index + i ] ) ;
} else
if ( i < 5 ) {
textCol . g * = 16 ;
textCol . g + = hexToNumber ( sText [ index + i ] ) ;
} else {
textCol . b * = 16 ;
textCol . b + = hexToNumber ( sText [ index + i ] ) ;
}
}
if ( textCol = = WHITE ) textCol = col ;
}
else
{
int32_t ox = ( c - 32 ) % 16 ;
int32_t oy = ( c - 32 ) / 16 ;
planningMarker . x + = float ( vFontSpacing [ c - 32 ] . y ) * scale . x ;
if ( planningMarker . x > width ) {
if ( drawingMarker . x = = 0 ) { //The text has overflowed a full line, so we have to dump the line.
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
letters . clear ( ) ;
} else {
drawingMarker . x - = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ; //Don't include the space in the sizing when wrapping.
}
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
drawingMarker = planningMarker ;
wrappingOccurred = true ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
planningMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
}
letters . emplace_back ( c , vf2d { float ( ox ) * 8.0f + float ( vFontSpacing [ c - 32 ] . x ) , float ( oy ) * 8.0f } , vf2d { float ( vFontSpacing [ c - 32 ] . y ) , 8.0f } , textCol ) ;
}
garbageCollector [ key ] . originalStr = originalKey ;
SetDrawTarget ( newDecal - > sprite ) ;
Clear ( BLANK ) ;
DrawStringProp ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
SetDrawTarget ( nullptr ) ;
newDecal - > Update ( ) ;
}
garbageCollector [ key ] . expireTime = GetRunTime ( ) + 120.0f ;
DrawDecal ( pos , garbageCollector [ key ] . decal , scale , GetFinalRenderColor ( col , sText ) ) ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialDecal ( pos + drawingMarker , fontRenderable . Decal ( ) , letter . sourcePos , letter . sourceSize , scale , letter . col ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
}
void PixelGameEngine : : DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const float width , const float shadowSizeFactor , const bool disableDynamicScaling ) {
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { stripCol ( sText ) } ;
std : : string renderStr { stripLeadingCol ( sText ) } ;
std : : string key { " DSSD_ " + std : : string ( originalKey ) } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = garbageCollector . count ( key ) & & garbageCollector [ key ] . originalStr ! = renderStr ;
const bool ShadowRerenderRequired = garbageCollector . count ( key + " _SHADOW " ) & & garbageCollector [ key + " _SHADOW " ] . originalStr ! = renderStr ;
if ( ! garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = GetWrappedTextSize ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = garbageCollector [ key ] . decal ;
}
garbageCollector [ key ] . originalStr = originalKey ;
SetDrawTarget ( newDecal - > sprite ) ;
Clear ( BLANK ) ;
DrawString ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
newDecal - > Update ( ) ;
vf2d adjustedShadowSizeFactor = vf2d { shadowSizeFactor , shadowSizeFactor } * 4 / scale ;
Decal * newShadowDecal = nullptr ;
if ( ! ShadowRerenderRequired ) {
newShadowDecal = new Decal ( new Sprite ( ( imageSize . x / scale . x * 4 ) + adjustedShadowSizeFactor . x * 2 , ( imageSize . y / scale . y * 4 ) + adjustedShadowSizeFactor . y * 2 ) ) ;
garbageCollector [ key + " _SHADOW " ] . decal = newShadowDecal ;
} else {
newShadowDecal = garbageCollector [ key + " _SHADOW " ] . decal ;
}
SetDrawTarget ( newShadowDecal - > sprite ) ;
Clear ( BLANK ) ;
for ( float y = - adjustedShadowSizeFactor . y ; y < = adjustedShadowSizeFactor . y + 0.1 ; y + = adjustedShadowSizeFactor . y / 2 ) {
for ( float x = - adjustedShadowSizeFactor . x ; x < = adjustedShadowSizeFactor . x + 0.1 ; x + = adjustedShadowSizeFactor . x / 2 ) {
if ( x ! = 0 | | y ! = 0 ) {
DrawString ( vf2d { x , y } + adjustedShadowSizeFactor , renderStr , WHITE , 4U , width / scale . x * 4 ) ;
void PixelGameEngine : : DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale ) {
DrawShadowStringDecal ( pos , sText , col , shadowCol , scale , scale ) ;
}
void PixelGameEngine : : DrawShadowStringDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const olc : : vf2d & shadowScale , const float width ) {
static std : : vector < PixelGameEngine : : StringDecalData > letters ;
letters . clear ( ) ;
bool wrappingOccurred = false ;
olc : : vf2d planningMarker = { 0.0f , 0.0f } ;
olc : : vf2d drawingMarker = { 0.0f , 0.0f } ;
const auto hexToNumber = [ ] ( char c ) {
if ( c < = ' 9 ' ) return c - ' 0 ' ;
return ( c - ' A ' ) + 10 ;
} ;
for ( int skip = 0 , index = - 1 ; auto c : sText )
{
index + + ;
if ( skip ) {
skip - - ;
continue ;
}
if ( c = = ' ' | | c = = ' \t ' | | c = = ' \n ' ) {
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
letters . clear ( ) ;
wrappingOccurred = false ;
}
if ( wrappingOccurred ) {
if ( c ! = ' ' & & c ! = ' \t ' & & c ! = ' \n ' ) {
wrappingOccurred = false ;
} else {
continue ;
}
}
if ( c = = ' \r ' ) continue ; //Ignore carriage returns. Stupid Linux.
if ( c = = ' \n ' )
{
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
letters . clear ( ) ;
drawingMarker . x = 0 ; drawingMarker . y + = 8.0f * scale . y ;
}
else if ( c = = ' ' )
{
drawingMarker . x + = 8.0f * scale . x ;
planningMarker . x + = 8.0f * scale . x ;
}
else if ( c = = ' \t ' )
{
drawingMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
planningMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
}
else if ( c > = - 128 & & c < - 105 ) { }
else if ( c = = PixelGameEngine : : Reset [ 0 ] ) { }
else if ( c = = ' # ' )
{
skip = 6 ;
}
else
{
int32_t ox = ( c - 32 ) % 16 ;
int32_t oy = ( c - 32 ) / 16 ;
planningMarker . x + = 8.0f * scale . x ;
if ( planningMarker . x > width ) {
if ( drawingMarker . x = = 0 ) { //The text has overflowed a full line, so we have to dump the line.
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
letters . clear ( ) ;
} else {
drawingMarker . x - = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ; //Don't include the space in the sizing when wrapping.
}
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
drawingMarker = planningMarker ;
wrappingOccurred = true ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
planningMarker . x + = 8.0f * scale . x ;
}
}
letters . emplace_back ( c , vf2d { float ( ox ) * 10.0f , float ( oy ) * 10.0f } , vf2d { 10.0f , 10.0f } , shadowCol ) ;
}
SetDrawTarget ( nullptr ) ;
newShadowDecal - > Update ( ) ;
}
garbageCollector [ key ] . expireTime = GetRunTime ( ) + 120.0f ;
garbageCollector [ key + " _SHADOW " ] . expireTime = GetRunTime ( ) + 120.0f ;
DrawDecal ( pos - vf2d { shadowSizeFactor , shadowSizeFactor } , garbageCollector [ key + " _SHADOW " ] . decal , scale / 4 , shadowCol ) ;
DrawDecal ( pos , garbageCollector [ key ] . decal , scale , GetFinalRenderColor ( col , sText ) ) ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = 8.0f * scale . x ;
}
DrawStringDecal ( pos , sText , col , scale , width ) ;
}
void PixelGameEngine : : DrawStringDecal ( Font & font , const olc : : vf2d & pos , const std : : u32string & sText , const Pixel col , const olc : : vf2d & scale ) {
@ -3609,7 +3820,7 @@ namespace olc
if ( ! garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete garbageCollector [ key ] . decal ;
garbageCollector [ key ] . decal = font . RenderStringToDecal ( renderStr , WHITE ) ;
garbageCollector [ key ] . originalStr = std : : string ( originalKey . begin ( ) , originalKey . end ( ) ) ;
garbageCollector [ key ] . originalStr = std : : string ( renderStr . begin ( ) , renderStr . end ( ) ) ;
}
garbageCollector [ key ] . expireTime = GetRunTime ( ) + 120.0f ;
DrawDecal ( pos , garbageCollector [ key ] . decal , scale / 4 , GetFinalRenderColor ( col , sText ) ) ;
@ -3625,7 +3836,7 @@ namespace olc
if ( ! garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete garbageCollector [ key ] . decal ;
garbageCollector [ key ] . decal = font . RenderStringToDecal ( renderStr , WHITE ) ;
garbageCollector [ key ] . originalStr = std : : string ( originalKey . begin ( ) , originalKey . end ( ) ) ;
garbageCollector [ key ] . originalStr = std : : string ( renderStr . begin ( ) , renderStr . end ( ) ) ;
}
garbageCollector [ key ] . expireTime = GetRunTime ( ) + 120.0f ;
for ( float y = - shadowSizeFactor ; y < = shadowSizeFactor + 0.1 ; y + = shadowSizeFactor / 2 ) {
@ -3648,7 +3859,7 @@ namespace olc
if ( ! garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete garbageCollector [ key ] . decal ;
garbageCollector [ key ] . decal = font . RenderStringToDecal ( renderStr , WHITE ) ;
garbageCollector [ key ] . originalStr = std : : string ( originalKey . begin ( ) , originalKey . end ( ) ) ;
garbageCollector [ key ] . originalStr = std : : string ( renderStr . begin ( ) , renderStr . end ( ) ) ;
}
garbageCollector [ key ] . expireTime = GetRunTime ( ) + 120.0f ;
DrawDecal ( pos + vf2d { 0 , 0.5f } , garbageCollector [ key ] . decal , scale / 4 , shadowCol ) ;
@ -3657,56 +3868,99 @@ namespace olc
DrawDecal ( pos , garbageCollector [ key ] . decal , scale / 4 , GetFinalRenderColor ( col , sText ) ) ;
}
void PixelGameEngine : : DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const float width , const float shadowSizeFactor , const bool disableDynamicScaling ) {
if ( sText . length ( ) = = 0 ) return ;
std : : string originalKey { stripCol ( sText ) } ;
std : : string renderStr { stripLeadingCol ( sText ) } ;
std : : string key { " DSSPD_ " + originalKey } ;
key + = std : : to_string ( width ) ;
if ( ! disableDynamicScaling ) {
key + = scale . str ( ) ;
}
const bool RerenderRequired = garbageCollector . count ( key ) & & garbageCollector [ key ] . originalStr ! = renderStr ;
const bool ShadowRerenderRequired = garbageCollector . count ( key + " _SHADOW " ) & & garbageCollector [ key + " _SHADOW " ] . originalStr ! = renderStr ;
if ( ! garbageCollector . count ( key ) | | RerenderRequired ) { //If the text key already exists, don't have to recreate the decal, just update the expire time.
vf2d imageSize = GetWrappedTextSizeProp ( originalKey , width , scale ) ;
if ( imageSize . x < 1 | | imageSize . y < 1 ) return ;
Decal * newDecal = nullptr ;
if ( ! RerenderRequired ) {
newDecal = new Decal ( new Sprite ( imageSize . x / scale . x , imageSize . y / scale . y ) ) ;
garbageCollector [ key ] . decal = newDecal ;
} else {
newDecal = garbageCollector [ key ] . decal ;
}
garbageCollector [ key ] . originalStr = originalKey ;
SetDrawTarget ( newDecal - > sprite ) ;
Clear ( BLANK ) ;
DrawStringProp ( { 0 , 0 } , renderStr , WHITE , 1U , width / scale . x ) ;
newDecal - > Update ( ) ;
vf2d adjustedShadowSizeFactor = vf2d { shadowSizeFactor , shadowSizeFactor } * 4 / scale ;
Decal * newShadowDecal = nullptr ;
if ( ! ShadowRerenderRequired ) {
newShadowDecal = new Decal ( new Sprite ( ( imageSize . x / scale . x * 4 ) + adjustedShadowSizeFactor . x * 2 , ( imageSize . y / scale . y * 4 ) + adjustedShadowSizeFactor . y * 2 ) ) ;
garbageCollector [ key + " _SHADOW " ] . decal = newShadowDecal ;
} else {
newShadowDecal = garbageCollector [ key + " _SHADOW " ] . decal ;
}
SetDrawTarget ( newShadowDecal - > sprite ) ;
Clear ( BLANK ) ;
for ( float y = - adjustedShadowSizeFactor . y ; y < = adjustedShadowSizeFactor . y + 0.1 ; y + = adjustedShadowSizeFactor . y / 2 ) {
for ( float x = - adjustedShadowSizeFactor . x ; x < = adjustedShadowSizeFactor . x + 0.1 ; x + = adjustedShadowSizeFactor . x / 2 ) {
if ( x ! = 0 | | y ! = 0 ) {
DrawStringProp ( vf2d { x , y } + adjustedShadowSizeFactor , renderStr , WHITE , 4U , width / scale . x * 4 ) ;
void PixelGameEngine : : DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale ) {
DrawShadowStringPropDecal ( pos , sText , col , shadowCol , scale , scale ) ;
}
void PixelGameEngine : : DrawShadowStringPropDecal ( const olc : : vf2d & pos , std : : string_view sText , const Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const olc : : vf2d & shadowScale , const float width ) {
static std : : vector < PixelGameEngine : : StringDecalData > letters ;
letters . clear ( ) ;
bool wrappingOccurred = false ;
olc : : vf2d planningMarker = { 0.0f , 0.0f } ;
olc : : vf2d drawingMarker = { 0.0f , 0.0f } ;
const auto hexToNumber = [ ] ( char c ) {
if ( c < = ' 9 ' ) return c - ' 0 ' ;
return ( c - ' A ' ) + 10 ;
} ;
for ( int skip = 0 , index = - 1 ; auto c : sText )
{
index + + ;
if ( skip ) {
skip - - ;
continue ;
}
if ( c = = ' ' | | c = = ' \t ' | | c = = ' \n ' ) {
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
letters . clear ( ) ;
wrappingOccurred = false ;
}
if ( wrappingOccurred ) {
if ( c ! = ' ' & & c ! = ' \t ' & & c ! = ' \n ' ) {
wrappingOccurred = false ;
} else {
continue ;
}
}
if ( c = = ' \r ' ) continue ; //Ignore carriage returns. Stupid Linux.
if ( c = = ' \n ' )
{
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
letters . clear ( ) ;
drawingMarker . x = 0 ; drawingMarker . y + = 8.0f * scale . y ;
}
else if ( c = = ' ' )
{
drawingMarker . x + = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ;
planningMarker . x + = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ;
}
else if ( c = = ' \t ' )
{
drawingMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
planningMarker . x + = 8.0f * float ( nTabSizeInSpaces ) * scale . x ;
}
else if ( c > = - 128 & & c < - 105 ) { }
else if ( c = = PixelGameEngine : : Reset [ 0 ] ) { }
else if ( c = = ' # ' )
{
skip = 6 ;
}
else
{
int32_t ox = ( c - 32 ) % 16 ;
int32_t oy = ( c - 32 ) / 16 ;
planningMarker . x + = float ( vFontSpacing [ c - 32 ] . y ) * scale . x ;
if ( planningMarker . x > width ) {
if ( drawingMarker . x = = 0 ) { //The text has overflowed a full line, so we have to dump the line.
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
letters . clear ( ) ;
} else {
drawingMarker . x - = float ( vFontSpacing [ ' ' - 32 ] . y ) * scale . x ; //Don't include the space in the sizing when wrapping.
}
planningMarker . x = 0 ; planningMarker . y + = 8.0f * scale . y ;
drawingMarker = planningMarker ;
wrappingOccurred = true ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
planningMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
}
letters . emplace_back ( c , vf2d { float ( ox ) * 10.0f + float ( vFontSpacing [ c - 32 ] . x ) , float ( oy ) * 10.0f } , vf2d { float ( vFontSpacing [ c - 32 ] . y ) + 2 , 10.0f } , shadowCol ) ;
}
SetDrawTarget ( nullptr ) ;
newShadowDecal - > Update ( ) ;
}
garbageCollector [ key ] . expireTime = GetRunTime ( ) + 120.0f ;
garbageCollector [ key + " _SHADOW " ] . expireTime = GetRunTime ( ) + 120.0f ;
DrawDecal ( pos - vf2d { shadowSizeFactor , shadowSizeFactor } , garbageCollector [ key + " _SHADOW " ] . decal , scale / 4 , shadowCol ) ;
DrawDecal ( pos , garbageCollector [ key ] . decal , scale , GetFinalRenderColor ( col , sText ) ) ;
for ( PixelGameEngine : : StringDecalData & letter : letters ) {
DrawPartialRotatedDecal ( pos + drawingMarker + vf2d { 5.f , 5.f } * scale - shadowScale , fontRenderableShadow . Decal ( ) , 0.f , { 5.f , 5.f } , letter . sourcePos , letter . sourceSize , shadowScale , shadowCol ) ;
drawingMarker . x + = float ( vFontSpacing [ letter . c - 32 ] . y ) * scale . x ;
}
DrawStringPropDecal ( pos , sText , col , scale , width ) ;
}
void PixelGameEngine : : DrawShadowString ( const olc : : vi2d & pos , std : : string_view sText , Pixel col , const Pixel shadowCol , const olc : : vf2d & scale , const float width , const float shadowSizeFactor ) {