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. 560
      Adventures in Lestoria/olcPGEX_ViewPort.h
  13. 566
      Adventures in Lestoria/olcPixelGameEngine.h
  14. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1994,7 +1994,7 @@ void AiL::RenderHud(){
std::stringstream castTimeDisplay;
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.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){
@ -2020,8 +2020,8 @@ void AiL::RenderHud(){
std::string text=player->GetHealth()>0?std::to_string(healthCounter.GetDisplayValue()):"X";
std::string text_mana=std::to_string(manaCounter.GetDisplayValue());
DrawShadowStringPropDecal({20,3},text,healthCounter.GetDisplayColor(),healthOutlineCol,{2,2},INFINITE);
DrawShadowStringPropDecal({24,23},text_mana,manaCounter.GetDisplayColor(),BLACK,{1.5f,1.5f},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},{1.45f,1.45f},INFINITE);
#pragma region Show Max Health/Max Mana
if(GameSettings::ShowMaxHealth()){
@ -2029,14 +2029,14 @@ void AiL::RenderHud(){
std::string maxHealthText="/"+std::to_string(int(player->GetMaxHealth()));
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()){
vf2d manaTextSize=GetTextSizeProp(text_mana)*vf2d{1.5f,1.5f};
std::string maxManaText="/"+std::to_string(player->GetMaxMana());
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
@ -2108,7 +2108,7 @@ void AiL::RenderCooldowns(){
}
std::stringstream cooldownTimeDisplay;
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{
vf2d iconScale={1,1};
if(loadoutSlot!=-1)iconScale={0.7f,0.7f};
@ -2148,7 +2148,7 @@ void AiL::RenderCooldowns(){
if(itemAmt>0){
std::string amtString="x"+std::to_string(itemAmt);
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{
DrawDecal(pos,GFX["square_skill_overlay_icon_empty.png"].Decal(),{1,1},DARK_RED);
shortNameCol=RED;
@ -2160,11 +2160,11 @@ void AiL::RenderCooldowns(){
if(a.manaCost>0){
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};
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};
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()){
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.");
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.");
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));
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(){

@ -68,8 +68,8 @@ protected:
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,ability->description,WHITE,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},{0.8f,1.f},int(rect.size.x-(descriptionPos.x-rect.pos.x))-4);
InputType controlType=KEY;
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){
std::string attributeLabel=std::format("{}",attr.Name());
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;
float nextStageValue=0;
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.
window.DrawShadowStringDecal(drawPos+vf2d{maxAttributeLabelSize+4+24,yOffset},std::format("{:<5}",(
(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.
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),
(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)
:"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;
}
}else{
std::string text="Cannot be enhanced.";
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 drawPos=rect.pos+rect.size-textSize;
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{
if(shadow){
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{
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{
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{
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));
}
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{
window.DrawStringPropDecal(drawPos,GetLabel(),WHITE,scale,int(rect.size.x-1));
}

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

@ -84,6 +84,7 @@ Images
GFX_Unlock = unlock.png
GFX_SwordSlash = swordslash.png
GFX_CustomFont = font.png
GFX_CustomFontShadow = font_shadow.png
GFX_Vignette = vignette.png
GFX_Checkmark = checkmark.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
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 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 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 = olc::WHITE, const Pixel shadowCol = olc::BLACK, const olc::vf2d& scale = { 1.0f, 1.0f });
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
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);
@ -644,12 +646,20 @@ namespace olc
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){
pge->DrawShadowStringDecal(WorldToScreen(pos),sText,col,shadowCol,scale,width,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,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){
pge->DrawShadowStringPropDecal(WorldToScreen(pos),sText,col,shadowCol,scale,width,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->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)

@ -116,11 +116,13 @@ namespace olc {
const vf2d &pos2,
Pixel p = WHITE) const;
// 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 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 });
@ -634,36 +636,116 @@ float olc::ViewPort::directionFromLine(vf2d lineA, vf2d lineB, vf2d point) {
- (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){
if(sText.length()==0)return;
std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSD_"+originalKey};
key+=std::to_string(width);
if(!disableDynamicScaling){
key+=scale.str();
}
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
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.
vf2d imageSize=pge->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));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
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();
void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width){
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, 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{
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, 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;
}
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
}
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));
}
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){
if(sText.length()==0)return;
std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSPD"+originalKey};
key+=std::to_string(width);
if(!disableDynamicScaling){
key+=scale.str();
}
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
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.
vf2d imageSize=pge->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));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
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();
void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width){
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, 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{
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, 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;
}
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
}
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){
if(sText.length()==0)return;
std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSSD_"+originalKey};
key+=std::to_string(width);
if(!disableDynamicScaling){
key+=scale.str();
}
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
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.
vf2d imageSize=pge->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));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK);
pge->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));
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal;
}
pge->SetDrawTarget(newShadowDecal->sprite);
pge->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){
pge->DrawString(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
void olc::ViewPort::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 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){
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, 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{
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, pge->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, pge->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-=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) * 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;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
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;
}
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){
if(sText.length()==0)return;
std::string originalKey{pge->stripCol(sText)};
std::string renderStr{pge->stripLeadingCol(sText)};
std::string key{"DSSPD"+originalKey};
key+=std::to_string(width);
if(!disableDynamicScaling){
key+=scale.str();
}
const bool RerenderRequired=pge->garbageCollector.count(key)&&pge->garbageCollector[key].originalStr!=renderStr;
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.
vf2d imageSize=pge->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));
pge->garbageCollector[key].decal=newDecal;
}else{
newDecal=pge->garbageCollector[key].decal;
}
pge->garbageCollector[key].originalStr=originalKey;
pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK);
pge->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));
pge->garbageCollector[key+"_SHADOW"].decal=newShadowDecal;
}else{
newShadowDecal=pge->garbageCollector[key+"_SHADOW"].decal;
}
pge->SetDrawTarget(newShadowDecal->sprite);
pge->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){
pge->DrawStringProp(vf2d{x,y}+adjustedShadowSizeFactor,renderStr,WHITE,4U,width/scale.x*4);
void olc::ViewPort::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 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){
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, 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{
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, 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();
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{
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 += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
}
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;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,pge->garbageCollector[key].decal,scale,pge->GetFinalRenderColor(col,sText));
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;
}
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){
@ -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.
delete pge->garbageCollector[key].decal;
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;
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.
delete pge->garbageCollector[key].decal;
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;
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 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){

Loading…
Cancel
Save