Change shadow text rendering system to use a pre-generated shadow font sprite to improve text rendering speeds dramatically. Fix bug from commit cfd73ab036 where custom font colored shadow text kept creating new versionf of itself causing memory leaks. Release Build 10791.

mac-build
sigonasr2 4 months ago
parent 00e22877c4
commit d49e7ff6bb
  1. 24
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 4
      Adventures in Lestoria/CharacterAbilityPreviewComponent.h
  3. 8
      Adventures in Lestoria/EnhancementStatsLabel.h
  4. 2
      Adventures in Lestoria/MenuItemItemButton.h
  5. 8
      Adventures in Lestoria/MenuLabel.h
  6. 2
      Adventures in Lestoria/PopupMenuLabel.h
  7. 2
      Adventures in Lestoria/Version.h
  8. 1
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  9. BIN
      Adventures in Lestoria/assets/font_shadow.png
  10. BIN
      Adventures in Lestoria/assets/gamepack.pak
  11. 22
      Adventures in Lestoria/olcPGEX_TransformedView.h
  12. 540
      Adventures in Lestoria/olcPGEX_ViewPort.h
  13. 550
      Adventures in Lestoria/olcPixelGameEngine.h
  14. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1994,7 +1994,7 @@ void AiL::RenderHud(){
std::stringstream castTimeDisplay; std::stringstream castTimeDisplay;
castTimeDisplay<<std::fixed<<std::setprecision(1)<<timer; castTimeDisplay<<std::fixed<<std::setprecision(1)<<timer;
DrawShadowStringPropDecal(vf2d{ScreenWidth()/2+90.f,ScreenHeight()-80.f}-vf2d{float(GetTextSizeProp(castTimeDisplay.str()).x),0},castTimeDisplay.str(),WHITE,BLACK); DrawShadowStringPropDecal(vf2d{ScreenWidth()/2+90.f,ScreenHeight()-80.f}-vf2d{float(GetTextSizeProp(castTimeDisplay.str()).x),0},castTimeDisplay.str(),WHITE,BLACK);
DrawShadowStringPropDecal(vf2d{ScreenWidth()/2.f-GetTextSizeProp(castText).x*2/2,ScreenHeight()-64.f},castText,WHITE,BLACK,{2,3},std::numeric_limits<float>::max(),2.f); DrawShadowStringPropDecal(vf2d{ScreenWidth()/2.f-GetTextSizeProp(castText).x*2/2,ScreenHeight()-64.f},castText,WHITE,BLACK,{2,3},{1.8f,2.6f},std::numeric_limits<float>::max());
}; };
if(GetPlayer()->GetCastInfo().castTimer>0){ if(GetPlayer()->GetCastInfo().castTimer>0){
@ -2020,8 +2020,8 @@ void AiL::RenderHud(){
std::string text=player->GetHealth()>0?std::to_string(healthCounter.GetDisplayValue()):"X"; std::string text=player->GetHealth()>0?std::to_string(healthCounter.GetDisplayValue()):"X";
std::string text_mana=std::to_string(manaCounter.GetDisplayValue()); std::string text_mana=std::to_string(manaCounter.GetDisplayValue());
DrawShadowStringPropDecal({20,3},text,healthCounter.GetDisplayColor(),healthOutlineCol,{2,2},INFINITE); DrawShadowStringPropDecal({20,3},text,healthCounter.GetDisplayColor(),healthOutlineCol,{2,2},{1.8f,1.8f},INFINITE);
DrawShadowStringPropDecal({24,23},text_mana,manaCounter.GetDisplayColor(),BLACK,{1.5f,1.5f},INFINITE); DrawShadowStringPropDecal({24,23},text_mana,manaCounter.GetDisplayColor(),BLACK,{1.5f,1.5f},{1.45f,1.45f},INFINITE);
#pragma region Show Max Health/Max Mana #pragma region Show Max Health/Max Mana
if(GameSettings::ShowMaxHealth()){ if(GameSettings::ShowMaxHealth()){
@ -2029,14 +2029,14 @@ void AiL::RenderHud(){
std::string maxHealthText="/"+std::to_string(int(player->GetMaxHealth())); std::string maxHealthText="/"+std::to_string(int(player->GetMaxHealth()));
float maxHealthTextHeight=GetTextSizeProp(maxHealthText).y; float maxHealthTextHeight=GetTextSizeProp(maxHealthText).y;
DrawShadowStringPropDecal(vf2d{20,3}+healthTextSize+vf2d{1.f,-maxHealthTextHeight},maxHealthText,{200,200,200,255},healthOutlineCol,{1.f,1.f},INFINITE); DrawShadowStringPropDecal(vf2d{20,3}+healthTextSize+vf2d{1.f,-maxHealthTextHeight},maxHealthText,{200,200,200,255},healthOutlineCol,{1.f,1.f},{1.f,1.f},INFINITE);
} }
if(GameSettings::ShowMaxMana()){ if(GameSettings::ShowMaxMana()){
vf2d manaTextSize=GetTextSizeProp(text_mana)*vf2d{1.5f,1.5f}; vf2d manaTextSize=GetTextSizeProp(text_mana)*vf2d{1.5f,1.5f};
std::string maxManaText="/"+std::to_string(player->GetMaxMana()); std::string maxManaText="/"+std::to_string(player->GetMaxMana());
float maxManaTextHeight=GetTextSizeProp(maxManaText).y; float maxManaTextHeight=GetTextSizeProp(maxManaText).y;
DrawShadowStringPropDecal(vf2d{24,23}+manaTextSize+vf2d{1.f,-maxManaTextHeight},maxManaText,{200,200,255,255},BLACK,{1.f,1.f},INFINITE); DrawShadowStringPropDecal(vf2d{24,23}+manaTextSize+vf2d{1.f,-maxManaTextHeight},maxManaText,{200,200,255,255},BLACK,{1.f,1.f},{1.f,1.f},INFINITE);
} }
#pragma endregion #pragma endregion
@ -2108,7 +2108,7 @@ void AiL::RenderCooldowns(){
} }
std::stringstream cooldownTimeDisplay; std::stringstream cooldownTimeDisplay;
cooldownTimeDisplay<<std::fixed<<std::setprecision(1)<<a.cooldown; cooldownTimeDisplay<<std::fixed<<std::setprecision(1)<<a.cooldown;
DrawShadowStringPropDecal(pos+vf2d{12,12}-vf2d{float(GetTextSizeProp(cooldownTimeDisplay.str()).x*0.5),float(GetTextSizeProp(cooldownTimeDisplay.str()).y*1)}/2,cooldownTimeDisplay.str(),WHITE,BLACK,{0.5,1}); DrawShadowStringPropDecal(pos+vf2d{12,12}-vf2d{float(GetTextSizeProp(cooldownTimeDisplay.str()).x*0.5),float(GetTextSizeProp(cooldownTimeDisplay.str()).y*1)}/2,cooldownTimeDisplay.str(),WHITE,BLACK,{0.5,1},{0.5,1});
}else{ }else{
vf2d iconScale={1,1}; vf2d iconScale={1,1};
if(loadoutSlot!=-1)iconScale={0.7f,0.7f}; if(loadoutSlot!=-1)iconScale={0.7f,0.7f};
@ -2148,7 +2148,7 @@ void AiL::RenderCooldowns(){
if(itemAmt>0){ if(itemAmt>0){
std::string amtString="x"+std::to_string(itemAmt); std::string amtString="x"+std::to_string(itemAmt);
vf2d qtySize=vf2d{GetTextSize(amtString)}*vf2d{0.5f,0.75f}; vf2d qtySize=vf2d{GetTextSize(amtString)}*vf2d{0.5f,0.75f};
DrawShadowStringDecal(pos+vf2d{20,20}-qtySize/2,amtString,WHITE,BLACK,{0.5f,0.75f}); DrawShadowStringDecal(pos+vf2d{20,20}-qtySize/2,amtString,WHITE,BLACK,{0.5f,0.75f},{0.5f,0.75f});
}else{ }else{
DrawDecal(pos,GFX["square_skill_overlay_icon_empty.png"].Decal(),{1,1},DARK_RED); DrawDecal(pos,GFX["square_skill_overlay_icon_empty.png"].Decal(),{1,1},DARK_RED);
shortNameCol=RED; shortNameCol=RED;
@ -2160,11 +2160,11 @@ void AiL::RenderCooldowns(){
if(a.manaCost>0){ if(a.manaCost>0){
vf2d manaCostSize=vf2d{GetTextSize(std::to_string(a.manaCost))}*vf2d{0.5f,0.75f}; vf2d manaCostSize=vf2d{GetTextSize(std::to_string(a.manaCost))}*vf2d{0.5f,0.75f};
DrawShadowStringDecal(pos+vf2d{20,4}-manaCostSize/2,std::to_string(a.manaCost),{192,192,255},manaCostShadowCol,{0.5f,0.75f}); DrawShadowStringDecal(pos+vf2d{20,4}-manaCostSize/2,std::to_string(a.manaCost),{192,192,255},manaCostShadowCol,{0.5f,0.75f},{0.5f,0.75f});
} }
vf2d shortNameSize=vf2d{GetTextSize(a.shortName)}*vf2d{0.5f,0.75f}; vf2d shortNameSize=vf2d{GetTextSize(a.shortName)}*vf2d{0.5f,0.75f};
DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,{255,255,255,230},{0.5f,0.75f}); DrawShadowStringDecal(pos+vf2d{13,24}-shortNameSize/2,a.shortName,shortNameCol,{255,255,255,230},{0.5f,0.75f},{0.5f,0.75f});
} }
}; };
@ -3594,7 +3594,7 @@ void AiL::DisplayBossEncounterInfo(){
} }
vf2d textScale={3,5}; vf2d textScale={3,5};
textScale.x=std::min(3.f,float(ScreenWidth())/GetTextSizeProp(displayText).x); textScale.x=std::min(3.f,float(ScreenWidth())/GetTextSizeProp(displayText).x);
DrawShadowStringPropDecal(vf2d{float(ScreenWidth()/2),float(ScreenHeight()/2)}-vf2d{GetTextSizeProp(displayText)}*textScale/2,displayText,{252, 186, 3, alpha},{128,0,0,alpha},textScale,std::numeric_limits<float>::max(),2); DrawShadowStringPropDecal(vf2d{float(ScreenWidth()/2),float(ScreenHeight()/2)}-vf2d{GetTextSizeProp(displayText)}*textScale/2,displayText,{252, 186, 3, alpha},{128,0,0,alpha},textScale,textScale*0.9f,std::numeric_limits<float>::max());
} }
if(InBossEncounter()){ if(InBossEncounter()){
Pixel displayCol=totalBossEncounterMobs==0?Pixel{224, 133, 29}:WHITE; Pixel displayCol=totalBossEncounterMobs==0?Pixel{224, 133, 29}:WHITE;
@ -3747,7 +3747,7 @@ void AiL::InitializeGraphics(){
} }
LOG(VisualNovel::graphicsToLoad.size()<<" images for visual novel engine have been loaded."); LOG(VisualNovel::graphicsToLoad.size()<<" images for visual novel engine have been loaded.");
SetFontSprite("GFX_Prefix"_S+"font.png",&gamepack,"GENERATE_GAMEPACK"_B); SetFontSprite("GFX_Prefix"_S+"font.png","GFX_Prefix"_S+"font_shadow.png",&gamepack,"GENERATE_GAMEPACK"_B);
LOG("Custom font loaded."); LOG("Custom font loaded.");
Menu::themes.SetInitialized(); Menu::themes.SetInitialized();
@ -3842,7 +3842,7 @@ void AiL::RenderVersionInfo(){
} }
std::string versionStr("v" + std::to_string(VERSION_MAJOR) + "." + std::to_string(VERSION_MINOR) + "." + std::to_string(VERSION_PATCH) + "." + std::to_string(VERSION_BUILD)); std::string versionStr("v" + std::to_string(VERSION_MAJOR) + "." + std::to_string(VERSION_MINOR) + "." + std::to_string(VERSION_PATCH) + "." + std::to_string(VERSION_BUILD));
DrawShadowStringDecal(vf2d{ GetScreenSize() } - vf2d{ GetTextSize(versionStr) }*0.4f,versionStr,ADMIN_MODE?RED:WHITE,BLACK,{0.4f,0.4f},std::numeric_limits<float>::max(),0.4f); DrawShadowStringDecal(vf2d{ GetScreenSize() } - vf2d{ GetTextSize(versionStr) }*0.4f,versionStr,ADMIN_MODE?RED:WHITE,BLACK,{0.4f,0.4f},{0.4f,0.4f},std::numeric_limits<float>::max());
} }
int AiL::GetCurrentChapter(){ int AiL::GetCurrentChapter(){

@ -68,8 +68,8 @@ protected:
vi2d descriptionPos=iconPos+vi2d{int(rect.size.y)-2,9}; vi2d descriptionPos=iconPos+vi2d{int(rect.size.y)-2,9};
window.DrawShadowStringPropDecal(descriptionPos-vf2d{0,10},ability->name,{0xFF,0xAF,0x56},BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4); window.DrawShadowStringPropDecal(descriptionPos-vf2d{0,10},ability->name,{0xFF,0xAF,0x56},BLACK,{0.8f,1.f},{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4); window.DrawShadowStringPropDecal(descriptionPos,ability->description,WHITE,BLACK,{0.8f,1.f},{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
InputType controlType=KEY; InputType controlType=KEY;
if(Input::UsingGamepad())controlType=CONTROLLER; if(Input::UsingGamepad())controlType=CONTROLLER;

@ -80,7 +80,7 @@ protected:
for(float yOffset=0;const auto&[attr,value]:itemRef.lock()->GetEnhancementInfo()[itemRef.lock()->EnhancementLevel()].stats){ for(float yOffset=0;const auto&[attr,value]:itemRef.lock()->GetEnhancementInfo()[itemRef.lock()->EnhancementLevel()].stats){
std::string attributeLabel=std::format("{}",attr.Name()); std::string attributeLabel=std::format("{}",attr.Name());
float attributeLabelWidth=game->GetTextSizeProp(attributeLabel).x; float attributeLabelWidth=game->GetTextSizeProp(attributeLabel).x;
window.DrawShadowStringPropDecal(drawPos+vf2d{maxAttributeLabelSize/2+24,yOffset}-vf2d{attributeLabelWidth/2,0},attributeLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f); window.DrawShadowStringPropDecal(drawPos+vf2d{maxAttributeLabelSize/2+24,yOffset}-vf2d{attributeLabelWidth/2,0},attributeLabel,WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
uint8_t nextEnhanceLevel=itemRef.lock()->EnhancementLevel()+1; uint8_t nextEnhanceLevel=itemRef.lock()->EnhancementLevel()+1;
float nextStageValue=0; float nextStageValue=0;
if(itemRef.lock()->GetEnhancementInfo().size()>nextEnhanceLevel){ if(itemRef.lock()->GetEnhancementInfo().size()>nextEnhanceLevel){
@ -92,21 +92,21 @@ protected:
if(Inventory::GetItemCount(itemRef.lock()->ActualName())==0){ //This item hasn't been created yet, so just show that we are developing the item first. if(Inventory::GetItemCount(itemRef.lock()->ActualName())==0){ //This item hasn't been created yet, so just show that we are developing the item first.
window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:<5}",( window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:<5}",(
(attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign)))), (attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign)))),
WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f); WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
}else{ //This item is getting enhanced to the next level. }else{ //This item is getting enhanced to the next level.
window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:>5} ->#00AA00 {:<5}", window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:>5} ->#00AA00 {:<5}",
attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign), attr.ShowAsDecimal()?std::format("{:>4.2f}{}",value,percentageSign):std::format("{}{}",value,percentageSign),
(nextEnhanceLevel<="Item.Item Max Enhancement Level"_I&&itemRef.lock()->GetEnhancementInfo()[nextEnhanceLevel].chapterAvailable<=game->GetCurrentChapter())? (nextEnhanceLevel<="Item.Item Max Enhancement Level"_I&&itemRef.lock()->GetEnhancementInfo()[nextEnhanceLevel].chapterAvailable<=game->GetCurrentChapter())?
attr.ShowAsDecimal()?std::format("{:<4.2f}{}",nextStageValue,percentageSign):std::format("{}{}",nextStageValue,percentageSign) attr.ShowAsDecimal()?std::format("{:<4.2f}{}",nextStageValue,percentageSign):std::format("{}{}",nextStageValue,percentageSign)
:"MAX"), :"MAX"),
WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f); WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
} }
yOffset+=16; yOffset+=16;
} }
}else{ }else{
std::string text="Cannot be enhanced."; std::string text="Cannot be enhanced.";
float textWidth=game->GetTextSizeProp(text).x; float textWidth=game->GetTextSizeProp(text).x;
window.DrawShadowStringPropDecal(rect.middle()-vf2d{textWidth,0}/2,text,RED,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x,1.0f); window.DrawShadowStringPropDecal(rect.middle()-vf2d{textWidth,0}/2,text,RED,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x);
} }
} }
}; };

@ -160,7 +160,7 @@ protected:
vf2d textSize=vf2d(game->GetTextSizeProp(quantityText))*quantityTextScale; vf2d textSize=vf2d(game->GetTextSizeProp(quantityText))*quantityTextScale;
vf2d drawPos=rect.pos+rect.size-textSize; vf2d drawPos=rect.pos+rect.size-textSize;
if(itemRef.lock()->Amt()!=INFINITE){ if(itemRef.lock()->Amt()!=INFINITE){
window.DrawShadowStringDecal(drawPos,quantityText,WHITE,BLACK,quantityTextScale); window.DrawShadowStringDecal(drawPos,quantityText,WHITE,BLACK,quantityTextScale,quantityTextScale);
} }
} }
} }

@ -138,15 +138,15 @@ protected:
}else{ }else{
if(shadow){ if(shadow){
if(proportional){ if(proportional){
window.DrawShadowStringPropDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f); window.DrawShadowStringPropDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4);
}else{ }else{
window.DrawShadowStringDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f); window.DrawShadowStringDecal(drawPos,finalLabel,WHITE,BLACK,adjustedScale,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4);
} }
}else{ }else{
if(proportional){ if(proportional){
window.DrawStringPropDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f); window.DrawStringPropDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4);
}else{ }else{
window.DrawStringDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4,1.0f); window.DrawStringDecal(drawPos,finalLabel,WHITE,adjustedScale,fitToLabel?std::numeric_limits<float>::max():rect.size.x-4);
} }
} }
} }

@ -73,7 +73,7 @@ protected:
window.DrawStringPropDecal(rect.pos+rect.size/2-game->GetWrappedTextSizeProp(GetLabel(),int(rect.size.x-1),scale)/2,GetLabel(),WHITE,{1,1},int(rect.size.x-1)); window.DrawStringPropDecal(rect.pos+rect.size/2-game->GetWrappedTextSizeProp(GetLabel(),int(rect.size.x-1),scale)/2,GetLabel(),WHITE,{1,1},int(rect.size.x-1));
} }
if(shadow){ if(shadow){
window.DrawShadowStringPropDecal(drawPos,GetLabel(),WHITE,BLACK,scale,int(rect.size.x-1)); window.DrawShadowStringPropDecal(drawPos,GetLabel(),WHITE,BLACK,scale,scale,int(rect.size.x-1));
}else{ }else{
window.DrawStringPropDecal(drawPos,GetLabel(),WHITE,scale,int(rect.size.x-1)); window.DrawStringPropDecal(drawPos,GetLabel(),WHITE,scale,int(rect.size.x-1));
} }

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1 #define VERSION_MAJOR 1
#define VERSION_MINOR 2 #define VERSION_MINOR 2
#define VERSION_PATCH 3 #define VERSION_PATCH 3
#define VERSION_BUILD 10737 #define VERSION_BUILD 10791
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -84,6 +84,7 @@ Images
GFX_Unlock = unlock.png GFX_Unlock = unlock.png
GFX_SwordSlash = swordslash.png GFX_SwordSlash = swordslash.png
GFX_CustomFont = font.png GFX_CustomFont = font.png
GFX_CustomFontShadow = font_shadow.png
GFX_Vignette = vignette.png GFX_Vignette = vignette.png
GFX_Checkmark = checkmark.png GFX_Checkmark = checkmark.png
GFX_DaggerStab = dagger_stab.png GFX_DaggerStab = dagger_stab.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

@ -182,8 +182,10 @@ namespace olc
// Draws a multiline string as a decal, with tiniting and scaling // Draws a multiline string as a decal, with tiniting and scaling
void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits<float>::max()); void DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits<float>::max());
void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f },const float width=std::numeric_limits<float>::max()); void DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const olc::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 = 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); 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 });
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); 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 = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f });
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());
// Draws a single shaded filled rectangle as a decal // Draws a single shaded filled rectangle as a decal
void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE);
void DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); void DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE);
@ -644,12 +646,20 @@ namespace olc
pge->DrawStringPropDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel,width); pge->DrawStringPropDecal(WorldToScreen(pos), sText, col, scale * m_vWorldScale * m_vRecipPixel,width);
} }
void TransformedView::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){ void TransformedView::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
pge->DrawShadowStringDecal(WorldToScreen(pos),sText,col,shadowCol,scale,width,shadowSizeFactor); pge->DrawShadowStringDecal(WorldToScreen(pos),sText,col,shadowCol,scale,scale);
} }
void TransformedView::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){ void TransformedView::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){
pge->DrawShadowStringPropDecal(WorldToScreen(pos),sText,col,shadowCol,scale,width,shadowSizeFactor); pge->DrawShadowStringDecal(WorldToScreen(pos),sText,col,shadowCol,scale,shadowScale,width);
}
void TransformedView::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
pge->DrawShadowStringPropDecal(WorldToScreen(pos),sText,col,shadowCol,scale,scale);
}
void TransformedView::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){
pge->DrawShadowStringPropDecal(WorldToScreen(pos),sText,col,shadowCol,scale,shadowScale,width);
} }
void TransformedView::FillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel col) void TransformedView::FillRectDecal(const olc::vf2d & pos, const olc::vf2d & size, const olc::Pixel col)

@ -116,11 +116,13 @@ namespace olc {
const vf2d &pos2, const vf2d &pos2,
Pixel p = WHITE) const; Pixel p = WHITE) const;
// Draws a multiline string as a decal, with tinting and scaling // Draws a multiline string as a decal, with tinting 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 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 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 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 = 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 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 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 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 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 }); 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 });
@ -634,36 +636,116 @@ float olc::ViewPort::directionFromLine(vf2d lineA, vf2d lineB, vf2d point) {
- (point.x - lineA.x) * (lineB.y - lineA.y); - (point.x - lineA.x) * (lineB.y - lineA.y);
} }
void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool disableDynamicScaling){ void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width){
if(sText.length()==0)return; Pixel textCol=col;
std::string originalKey{pge->stripCol(sText)}; static std::vector<PixelGameEngine::StringDecalData>letters;
std::string renderStr{pge->stripLeadingCol(sText)}; letters.clear();
std::string key{"DSD_"+originalKey}; bool wrappingOccurred=false;
key+=std::to_string(width); olc::vf2d planningMarker = { 0.0f, 0.0f };
if(!disableDynamicScaling){ olc::vf2d drawingMarker = { 0.0f, 0.0f };
key+=scale.str(); const auto hexToNumber=[](char c){
} if(c<='9')return c-'0';
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; return (c-'A')+10;
const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr; };
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. for (int skip=0,index=-1;auto c : sText)
vf2d imageSize=pge->GetWrappedTextSize(originalKey,width,scale); {
if(imageSize.x<1||imageSize.y<1)return; index++;
Decal*newDecal=nullptr; if(skip){
if(!RerenderRequired){ skip--;
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); continue;
pge->garbageCollector[key].decal=newDecal; }
if(c==' '||c=='\t'||c=='\n'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->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{ }else{
newDecal=pge->garbageCollector[key].decal; continue;
} }
pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK);
pge->DrawString({0,0},renderStr,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr);
newDecal->Update();
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText)); if (c == '\n')
{
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->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, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
}else{
drawingMarker.x-=pge->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);
}
}
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col);
drawingMarker.x += 8.0f * scale.x;
}
} }
void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){ void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const olc::vf2d& scale){
@ -682,140 +764,306 @@ void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::
DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,pge->GetFinalRenderColor(col,sText)); DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,pge->GetFinalRenderColor(col,sText));
} }
void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool disableDynamicScaling){ void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width){
if(sText.length()==0)return; Pixel textCol=col;
std::string originalKey{pge->stripCol(sText)}; static std::vector<PixelGameEngine::StringDecalData>letters;
std::string renderStr{pge->stripLeadingCol(sText)}; letters.clear();
std::string key{"DSPD"+originalKey}; bool wrappingOccurred=false;
key+=std::to_string(width); olc::vf2d planningMarker = { 0.0f, 0.0f };
if(!disableDynamicScaling){ olc::vf2d drawingMarker = { 0.0f, 0.0f };
key+=scale.str(); const auto hexToNumber=[](char c){
} if(c<='9')return c-'0';
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; return (c-'A')+10;
const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr; };
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. for (int skip=0,index=-1;auto c : sText)
vf2d imageSize=pge->GetWrappedTextSizeProp(originalKey,width,scale); {
if(imageSize.x<1||imageSize.y<1)return; index++;
Decal*newDecal=nullptr; if(skip){
if(!RerenderRequired){ skip--;
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); continue;
pge->garbageCollector[key].decal=newDecal; }
if(c==' '||c=='\t'||c=='\n'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
wrappingOccurred=false;
}
if(wrappingOccurred){
if(c!=' '&&c!='\t'&&c!='\n'){
wrappingOccurred=false;
}else{ }else{
newDecal=pge->garbageCollector[key].decal; continue;
} }
pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK);
pge->DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr);
newDecal->Update();
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText)); if (c == '\n')
{
planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y;
}
else if (c == ' ')
{
drawingMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x;
planningMarker.x += float(pge->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(pge->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, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale,letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
}else{
drawingMarker.x-=float(pge->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(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
}
letters.emplace_back(c,vf2d{ float(ox) * 8.0f + float(pge->vFontSpacing[c - 32].x), float(oy) * 8.0f }, vf2d{ float(pge->vFontSpacing[c - 32].y), 8.0f },textCol);
}
}
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
} }
void olc::ViewPort::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){ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
if(sText.length()==0)return; DrawShadowStringDecal(pos,sText,col,shadowCol,scale,scale);
std::string originalKey{pge->stripCol(sText)}; }
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSSD_"+originalKey}; void olc::ViewPort::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){
key+=std::to_string(width); static std::vector<PixelGameEngine::StringDecalData>letters;
if(!disableDynamicScaling){ letters.clear();
key+=scale.str(); bool wrappingOccurred=false;
} olc::vf2d planningMarker = { 0.0f, 0.0f };
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; olc::vf2d drawingMarker = { 0.0f, 0.0f };
const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr; const auto hexToNumber=[](char c){
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(c<='9')return c-'0';
vf2d imageSize=pge->GetWrappedTextSize(originalKey,width,scale); return (c-'A')+10;
if(imageSize.x<1||imageSize.y<1)return; };
Decal*newDecal=nullptr; for (int skip=0,index=-1;auto c : sText)
if(!RerenderRequired){ {
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); index++;
pge->garbageCollector[key].decal=newDecal; 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, pge->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{ }else{
newDecal=pge->garbageCollector[key].decal; continue;
} }
pge->garbageCollector[key].originalStr=originalKey; }
pge->SetDrawTarget(newDecal->sprite); if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
pge->Clear(BLANK); if (c == '\n')
pge->DrawString({0,0},renderStr,WHITE,1U,width/scale.x); {
newDecal->Update(); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; for(PixelGameEngine::StringDecalData&letter:letters){
Decal*newShadowDecal=nullptr; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
if(!ShadowRerenderRequired){ drawingMarker.x += 8.0f * scale.x;
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2)); }
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; 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, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
}else{ }else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal; drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping.
} }
pge->SetDrawTarget(newShadowDecal->sprite); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
pge->Clear(BLANK); drawingMarker=planningMarker;
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ wrappingOccurred=true;
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(PixelGameEngine::StringDecalData&letter:letters){
if(x!=0||y!=0){ planningMarker.x += 8.0f * scale.x;
pge->DrawString(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
letters.emplace_back(c,vf2d{ float(ox) * 10.0f, float(oy) * 10.0f }, vf2d{ 10.0f, 10.0f },shadowCol);
} }
pge->SetDrawTarget(nullptr);
newShadowDecal->Update();
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; for(PixelGameEngine::StringDecalData&letter:letters){
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); drawingMarker.x += 8.0f * scale.x;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText)); }
DrawStringDecal(pos,sText,col,scale,width);
} }
void olc::ViewPort::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){ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
if(sText.length()==0)return; DrawShadowStringPropDecal(pos,sText,col,shadowCol,scale,scale);
std::string originalKey{pge->stripCol(sText)}; }
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSSPD"+originalKey}; void olc::ViewPort::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){
key+=std::to_string(width); static std::vector<PixelGameEngine::StringDecalData>letters;
if(!disableDynamicScaling){ letters.clear();
key+=scale.str(); bool wrappingOccurred=false;
} olc::vf2d planningMarker = { 0.0f, 0.0f };
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr; olc::vf2d drawingMarker = { 0.0f, 0.0f };
const bool ShadowRerenderRequired=pge->garbageCollector.count(key+"_SHADOW")&&pge->garbageCollector[key+"_SHADOW"].originalStr!=renderStr; const auto hexToNumber=[](char c){
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(c<='9')return c-'0';
vf2d imageSize=pge->GetWrappedTextSizeProp(originalKey,width,scale); return (c-'A')+10;
if(imageSize.x<1||imageSize.y<1)return; };
Decal*newDecal=nullptr; for (int skip=0,index=-1;auto c : sText)
if(!RerenderRequired){ {
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); index++;
pge->garbageCollector[key].decal=newDecal; 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, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
wrappingOccurred=false;
}
if(wrappingOccurred){
if(c!=' '&&c!='\t'&&c!='\n'){
wrappingOccurred=false;
}else{ }else{
newDecal=pge->garbageCollector[key].decal; continue;
} }
pge->garbageCollector[key].originalStr=originalKey; }
pge->SetDrawTarget(newDecal->sprite); if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
pge->Clear(BLANK); if (c == '\n')
pge->DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x); {
newDecal->Update(); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; for(PixelGameEngine::StringDecalData&letter:letters){
Decal*newShadowDecal=nullptr; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
if(!ShadowRerenderRequired){ drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
newShadowDecal=new Decal(new Sprite((imageSize.x/scale.x*4)+adjustedShadowSizeFactor.x*2,(imageSize.y/scale.y*4)+adjustedShadowSizeFactor.y*2)); }
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal; letters.clear();
drawingMarker.x = 0; drawingMarker.y += 8.0f * scale.y;
}
else if (c == ' ')
{
drawingMarker.x += float(pge->vFontSpacing[' ' - 32].y) * scale.x;
planningMarker.x += float(pge->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(pge->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, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
}else{ }else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal; drawingMarker.x-=pge->vFontSpacing[' '-32].y*scale.x; //Don't include the space in the sizing when wrapping.
} }
pge->SetDrawTarget(newShadowDecal->sprite); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
pge->Clear(BLANK); drawingMarker=planningMarker;
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ wrappingOccurred=true;
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(PixelGameEngine::StringDecalData&letter:letters){
if(x!=0||y!=0){ planningMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
pge->DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
letters.emplace_back(c,vf2d{ float(ox) * 10.0f + float(pge->vFontSpacing[c - 32].x), float(oy) * 10.0f }, vf2d{ float(pge->vFontSpacing[c - 32].y)+2, 10.0f },shadowCol);
} }
pge->SetDrawTarget(nullptr);
newShadowDecal->Update();
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; for(PixelGameEngine::StringDecalData&letter:letters){
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, pge->fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText)); }
DrawStringPropDecal(pos,sText,col,scale,width);
} }
void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){ void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const std::u32string& sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale,const float shadowSizeFactor){
@ -828,7 +1076,7 @@ void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal; delete pge->garbageCollector[key].decal;
pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end()); pge->garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end());
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
std::erase_if(pge->garbageCollector,[&](auto&key){ std::erase_if(pge->garbageCollector,[&](auto&key){
@ -858,7 +1106,7 @@ void olc::ViewPort::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, c
if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(!pge->garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
delete pge->garbageCollector[key].decal; delete pge->garbageCollector[key].decal;
pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); pge->garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE);
pge->garbageCollector[key].originalStr=std::string(originalKey.begin(),originalKey.end()); pge->garbageCollector[key].originalStr=std::string(renderStr.begin(),renderStr.end());
} }
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f; pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol); DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol);

@ -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 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); 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 // 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 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 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 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 = 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 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 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 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 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 }); 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 // Draws a single shaded filled rectangle as a decal
@ -1182,7 +1184,7 @@ namespace olc
olc::Sprite* GetFontSprite(); olc::Sprite* GetFontSprite();
// Returns the font image // Returns the font image
olc::Decal* GetFontDecal(); 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 // Clip a line segment to visible area
bool ClipLineToScreen(olc::vi2d& in_p1, olc::vi2d& in_p2); bool ClipLineToScreen(olc::vi2d& in_p1, olc::vi2d& in_p2);
@ -1311,7 +1313,7 @@ namespace olc
float fLastElapsed = 0.0f; float fLastElapsed = 0.0f;
int nFrameCount = 0; int nFrameCount = 0;
bool bSuspendTextureTransfer = false; bool bSuspendTextureTransfer = false;
Renderable fontRenderable; Renderable fontRenderable,fontRenderableShadow;
std::vector<LayerDesc> vLayers; std::vector<LayerDesc> vLayers;
uint8_t nTargetLayer = 0; uint8_t nTargetLayer = 0;
uint32_t nLastFPS = 0; uint32_t nLastFPS = 0;
@ -2474,16 +2476,20 @@ namespace olc
{ return fontRenderable.Sprite(); } { return fontRenderable.Sprite(); }
olc::Decal* PixelGameEngine::GetFontDecal() olc::Decal* PixelGameEngine::GetFontDecal()
{ return fontRenderable.Decal(); } { 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()){ if(pack->Loaded()){
fontRenderable.Load(std::string(filename),pack); fontRenderable.Load(std::string(filename),pack);
fontRenderableShadow.Load(std::string(shadowFilename),pack);
}else{ }else{
fontRenderable.Load(std::string(filename),nullptr); fontRenderable.Load(std::string(filename),nullptr);
fontRenderableShadow.Load(std::string(shadowFilename),nullptr);
if(generateGamePack){ if(generateGamePack){
pack->AddFile(std::string(filename)); pack->AddFile(std::string(filename));
pack->AddFile(std::string(shadowFilename));
} }
} }
//Generate shadow version.
} }
bool PixelGameEngine::ClipLineToScreen(olc::vi2d& in_p1, olc::vi2d& in_p2) 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; Pixel textCol=col;
std::string originalKey{stripCol(sText)}; static std::vector<PixelGameEngine::StringDecalData>letters;
std::string renderStr{stripLeadingCol(sText)}; letters.clear();
std::string key{"DSD"+std::string(originalKey)}; bool wrappingOccurred=false;
key+=std::to_string(width); olc::vf2d planningMarker = { 0.0f, 0.0f };
if(!disableDynamicScaling){ olc::vf2d drawingMarker = { 0.0f, 0.0f };
key+=scale.str(); const auto hexToNumber=[](char c){
} if(c<='9')return c-'0';
const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr; return (c-'A')+10;
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); for (int skip=0,index=-1;auto c : sText)
if(imageSize.x<1||imageSize.y<1)return; {
Decal*newDecal=nullptr; index++;
if(!RerenderRequired){ if(skip){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); skip--;
garbageCollector[key].decal=newDecal; 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{ }else{
newDecal=garbageCollector[key].decal; continue;
} }
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; if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); 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);
}
}
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; Pixel textCol=col;
std::string originalKey{stripCol(sText)}; static std::vector<PixelGameEngine::StringDecalData>letters;
std::string renderStr{stripLeadingCol(sText)}; letters.clear();
std::string key{"DSPD_"+std::string(originalKey)}; bool wrappingOccurred=false;
key+=std::to_string(width); olc::vf2d planningMarker = { 0.0f, 0.0f };
if(!disableDynamicScaling){ olc::vf2d drawingMarker = { 0.0f, 0.0f };
key+=scale.str(); const auto hexToNumber=[](char c){
} if(c<='9')return c-'0';
const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr; return (c-'A')+10;
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); for (int skip=0,index=-1;auto c : sText)
if(imageSize.x<1||imageSize.y<1)return; {
Decal*newDecal=nullptr; index++;
if(!RerenderRequired){ if(skip){
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); skip--;
garbageCollector[key].decal=newDecal; 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{ }else{
newDecal=garbageCollector[key].decal; continue;
} }
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; if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); 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);
}
}
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){ void PixelGameEngine::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
if(sText.length()==0)return; DrawShadowStringDecal(pos,sText,col,shadowCol,scale,scale);
std::string originalKey{stripCol(sText)}; }
std::string renderStr{stripLeadingCol(sText)};
std::string key{"DSSD_"+std::string(originalKey)}; 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){
key+=std::to_string(width); static std::vector<PixelGameEngine::StringDecalData>letters;
if(!disableDynamicScaling){ letters.clear();
key+=scale.str(); bool wrappingOccurred=false;
} olc::vf2d planningMarker = { 0.0f, 0.0f };
const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr; olc::vf2d drawingMarker = { 0.0f, 0.0f };
const bool ShadowRerenderRequired=garbageCollector.count(key+"_SHADOW")&&garbageCollector[key+"_SHADOW"].originalStr!=renderStr; const auto hexToNumber=[](char c){
if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(c<='9')return c-'0';
vf2d imageSize=GetWrappedTextSize(originalKey,width,scale); return (c-'A')+10;
if(imageSize.x<1||imageSize.y<1)return; };
Decal*newDecal=nullptr; for (int skip=0,index=-1;auto c : sText)
if(!RerenderRequired){ {
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); index++;
garbageCollector[key].decal=newDecal; 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{ }else{
newDecal=garbageCollector[key].decal; continue;
} }
garbageCollector[key].originalStr=originalKey; }
SetDrawTarget(newDecal->sprite); if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
Clear(BLANK); if (c == '\n')
DrawString({0,0},renderStr,WHITE,1U,width/scale.x); {
newDecal->Update(); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; for(PixelGameEngine::StringDecalData&letter:letters){
Decal*newShadowDecal=nullptr; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
if(!ShadowRerenderRequired){ drawingMarker.x += 8.0f * scale.x;
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; 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{ }else{
newShadowDecal=garbageCollector[key+"_SHADOW"].decal; drawingMarker.x-=float(vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping.
} }
SetDrawTarget(newShadowDecal->sprite); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
Clear(BLANK); drawingMarker=planningMarker;
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ wrappingOccurred=true;
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(PixelGameEngine::StringDecalData&letter:letters){
if(x!=0||y!=0){ planningMarker.x += 8.0f * scale.x;
DrawString(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
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; for(PixelGameEngine::StringDecalData&letter:letters){
garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); drawingMarker.x += 8.0f * scale.x;
DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); }
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){ 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. 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; delete garbageCollector[key].decal;
garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); 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; garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos,garbageCollector[key].decal,scale/4,GetFinalRenderColor(col,sText)); 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. 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; delete garbageCollector[key].decal;
garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); 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; garbageCollector[key].expireTime=GetRunTime()+120.0f;
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ 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. 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; delete garbageCollector[key].decal;
garbageCollector[key].decal=font.RenderStringToDecal(renderStr,WHITE); 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; garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos+vf2d{0,0.5f},garbageCollector[key].decal,scale/4,shadowCol); 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)); 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){ void PixelGameEngine::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const Pixel shadowCol, const olc::vf2d& scale){
if(sText.length()==0)return; DrawShadowStringPropDecal(pos,sText,col,shadowCol,scale,scale);
std::string originalKey{stripCol(sText)}; }
std::string renderStr{stripLeadingCol(sText)};
std::string key{"DSSPD_"+originalKey}; 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){
key+=std::to_string(width); static std::vector<PixelGameEngine::StringDecalData>letters;
if(!disableDynamicScaling){ letters.clear();
key+=scale.str(); bool wrappingOccurred=false;
} olc::vf2d planningMarker = { 0.0f, 0.0f };
const bool RerenderRequired=garbageCollector.count(key)&&garbageCollector[key].originalStr!=renderStr; olc::vf2d drawingMarker = { 0.0f, 0.0f };
const bool ShadowRerenderRequired=garbageCollector.count(key+"_SHADOW")&&garbageCollector[key+"_SHADOW"].originalStr!=renderStr; const auto hexToNumber=[](char c){
if(!garbageCollector.count(key)||RerenderRequired){ //If the text key already exists, don't have to recreate the decal, just update the expire time. if(c<='9')return c-'0';
vf2d imageSize=GetWrappedTextSizeProp(originalKey,width,scale); return (c-'A')+10;
if(imageSize.x<1||imageSize.y<1)return; };
Decal*newDecal=nullptr; for (int skip=0,index=-1;auto c : sText)
if(!RerenderRequired){ {
newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.y)); index++;
garbageCollector[key].decal=newDecal; 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{ }else{
newDecal=garbageCollector[key].decal; continue;
} }
garbageCollector[key].originalStr=originalKey; }
SetDrawTarget(newDecal->sprite); if(c=='\r')continue; //Ignore carriage returns. Stupid Linux.
Clear(BLANK); if (c == '\n')
DrawStringProp({0,0},renderStr,WHITE,1U,width/scale.x); {
newDecal->Update(); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
vf2d adjustedShadowSizeFactor=vf2d{shadowSizeFactor,shadowSizeFactor}*4/scale; for(PixelGameEngine::StringDecalData&letter:letters){
Decal*newShadowDecal=nullptr; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
if(!ShadowRerenderRequired){ drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x;
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; 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{ }else{
newShadowDecal=garbageCollector[key+"_SHADOW"].decal; drawingMarker.x-=float(vFontSpacing[' '-32].y)*scale.x; //Don't include the space in the sizing when wrapping.
} }
SetDrawTarget(newShadowDecal->sprite); planningMarker.x = 0; planningMarker.y += 8.0f * scale.y;
Clear(BLANK); drawingMarker=planningMarker;
for(float y=-adjustedShadowSizeFactor.y;y<=adjustedShadowSizeFactor.y+0.1;y+=adjustedShadowSizeFactor.y/2){ wrappingOccurred=true;
for(float x=-adjustedShadowSizeFactor.x;x<=adjustedShadowSizeFactor.x+0.1;x+=adjustedShadowSizeFactor.x/2){ for(PixelGameEngine::StringDecalData&letter:letters){
if(x!=0||y!=0){ planningMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x;
DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
} }
} }
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; for(PixelGameEngine::StringDecalData&letter:letters){
garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f; DrawPartialRotatedDecal(pos + drawingMarker+vf2d{5.f,5.f}*scale-shadowScale, fontRenderableShadow.Decal(),0.f,{5.f,5.f}, letter.sourcePos, letter.sourceSize,shadowScale,shadowCol);
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol); drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x;
DrawDecal(pos,garbageCollector[key].decal,scale,GetFinalRenderColor(col,sText)); }
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){ 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){

Loading…
Cancel
Save