Completed image caching technique for rendering text using the normal engine draw functions.

pull/28/head
sigonasr2 11 months ago
parent 8581633cab
commit 5d1e0b5a7a
  1. 2
      Crawler/Crawler.cpp
  2. 3
      Crawler/Key.cpp
  3. 5
      Crawler/Menu.cpp
  4. 2
      Crawler/MenuLabel.h
  5. 11
      Crawler/PopupMenuLabel.h
  6. 4
      Crawler/State_GameRun.cpp
  7. 2
      Crawler/Version.h
  8. 256
      Crawler/olcPGEX_ViewPort.h
  9. 281
      Crawler/olcPixelGameEngine.h
  10. BIN
      x64/Release/Crawler.exe

@ -1213,7 +1213,7 @@ void Crawler::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},2.f); 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);
}; };
if(GetPlayer()->GetCastInfo().castTimer>0){ if(GetPlayer()->GetCastInfo().castTimer>0){

@ -45,6 +45,7 @@ Input::Input(InputType type,int key)
:type(type),key(key){} :type(type),key(key){}
bool Input::Pressed(){ bool Input::Pressed(){
if(!game->IsFocused())return false;
switch(type){ switch(type){
case KEY:{ case KEY:{
return game->GetKey(Key(key)).bPressed; return game->GetKey(Key(key)).bPressed;
@ -61,6 +62,7 @@ bool Input::Pressed(){
} }
bool Input::Held(){ bool Input::Held(){
if(!game->IsFocused())return false;
switch(type){ switch(type){
case KEY:{ case KEY:{
return game->GetKey(Key(key)).bHeld; return game->GetKey(Key(key)).bHeld;
@ -77,6 +79,7 @@ bool Input::Held(){
} }
bool Input::Released(){ bool Input::Released(){
if(!game->IsFocused())return false;
switch(type){ switch(type){
case KEY:{ case KEY:{
return game->GetKey(Key(key)).bReleased; return game->GetKey(Key(key)).bReleased;

@ -144,7 +144,7 @@ void Menu::CheckClickAndPerformMenuSelect(Crawler*game){
} }
void Menu::HoverMenuSelect(Crawler*game){ void Menu::HoverMenuSelect(Crawler*game){
if(selection==vi2d{-1,-1}||buttons[selection.y][selection.x]->disabled)return; if(!game->IsFocused()||selection==vi2d{-1,-1}||buttons[selection.y][selection.x]->disabled)return;
if(buttons[selection.y][selection.x]->draggable){ if(buttons[selection.y][selection.x]->draggable){
if(buttonHoldTime<"ThemeGlobal.MenuHoldTime"_F){ if(buttonHoldTime<"ThemeGlobal.MenuHoldTime"_F){
CheckClickAndPerformMenuSelect(game); CheckClickAndPerformMenuSelect(game);
@ -158,7 +158,7 @@ void Menu::HoverMenuSelect(Crawler*game){
} }
void Menu::MenuSelect(Crawler*game){ void Menu::MenuSelect(Crawler*game){
if(selection==vi2d{-1,-1}||(buttons[selection.y][selection.x]->disabled||buttons[selection.y][selection.x]->grayedOut))return; if(!game->IsFocused()||selection==vi2d{-1,-1}||(buttons[selection.y][selection.x]->disabled||buttons[selection.y][selection.x]->grayedOut))return;
bool buttonStillValid=buttons[selection.y][selection.x]->onClick(MenuFuncData{*this,game,buttons[selection.y][selection.x],(ScrollableWindowComponent*)buttons[selection.y][selection.x]->parentComponent}); bool buttonStillValid=buttons[selection.y][selection.x]->onClick(MenuFuncData{*this,game,buttons[selection.y][selection.x],(ScrollableWindowComponent*)buttons[selection.y][selection.x]->parentComponent});
if(buttonStillValid){ if(buttonStillValid){
if(buttons[selection.y][selection.x]->menuDest!=MenuType::ENUM_END){ if(buttons[selection.y][selection.x]->menuDest!=MenuType::ENUM_END){
@ -253,6 +253,7 @@ void Menu::Update(Crawler*game){
} }
KeyboardButtonNavigation(game,pos); KeyboardButtonNavigation(game,pos);
for(auto&[key,value]:buttons){ for(auto&[key,value]:buttons){
for(auto&button:value){ for(auto&button:value){
if(button->renderInMain){ if(button->renderInMain){

@ -67,7 +67,7 @@ protected:
inline virtual void DrawDecal(ViewPort&window,bool focused)override{ inline virtual void DrawDecal(ViewPort&window,bool focused)override{
MenuComponent::DrawDecal(window,focused); MenuComponent::DrawDecal(window,focused);
vf2d adjustedScale={scale,scale}; vf2d adjustedScale={scale,scale};
vf2d labelTextSize=vf2d(game->GetWrappedTextSizeProp(label,rect.size.x,adjustedScale.x)); vf2d labelTextSize=vf2d(game->GetWrappedTextSizeProp(label,rect.size.x,adjustedScale));
if(fitToLabel){ if(fitToLabel){
float sizeRatio=((labelTextSize*adjustedScale).x)/(rect.size.x-2); float sizeRatio=((labelTextSize*adjustedScale).x)/(rect.size.x-2);

@ -59,10 +59,9 @@ protected:
} }
virtual void inline DrawDecal(ViewPort&window,bool focused)override{ virtual void inline DrawDecal(ViewPort&window,bool focused)override{
if(label.length()>0){ if(label.length()>0){
std::string wrappedText=util::WrapText(game,label,int(rect.size.x-1),true,scale); vf2d drawPos=rect.middle()-vf2d{game->GetWrappedTextSizeProp(label,int(rect.size.x-1),scale)}*scale/2; //Assume centered.
vf2d drawPos=rect.middle()-vf2d{game->GetTextSizeProp(wrappedText)}*scale/2; //Assume centered.
if(!centered){ if(!centered){
drawPos=vf2d{rect.pos.x+2,rect.middle().y-game->GetTextSizeProp(wrappedText).y/2}; //We should at least vertically align here. drawPos=vf2d{rect.pos.x+2,rect.middle().y-game->GetWrappedTextSizeProp(label,int(rect.size.x-1),scale).y/2}; //We should at least vertically align here.
} }
if(background){ if(background){
window.FillRectDecal(rect.pos,rect.size,PixelLerp(Menu::themes[Menu::themeSelection].GetButtonCol(),Menu::themes[Menu::themeSelection].GetHighlightCol(),hoverEffect/"ThemeGlobal.HighlightTime"_F)); window.FillRectDecal(rect.pos,rect.size,PixelLerp(Menu::themes[Menu::themeSelection].GetButtonCol(),Menu::themes[Menu::themeSelection].GetHighlightCol(),hoverEffect/"ThemeGlobal.HighlightTime"_F));
@ -71,12 +70,12 @@ protected:
window.DrawRectDecal(rect.pos,rect.size); window.DrawRectDecal(rect.pos,rect.size);
} }
if(showDefaultLabel){ if(showDefaultLabel){
window.DrawStringPropDecal(rect.pos+rect.size/2-game->GetTextSizeProp(label)/2,label); window.DrawStringPropDecal(rect.pos+rect.size/2-game->GetWrappedTextSizeProp(label,int(rect.size.x-1),scale)/2,label,WHITE,{1,1},int(rect.size.x-1));
} }
if(shadow){ if(shadow){
window.DrawShadowStringPropDecal(drawPos,wrappedText,WHITE,BLACK,scale); window.DrawShadowStringPropDecal(drawPos,label,WHITE,BLACK,scale,int(rect.size.x-1));
}else{ }else{
window.DrawStringPropDecal(drawPos,wrappedText,WHITE,scale); window.DrawStringPropDecal(drawPos,label,WHITE,scale,int(rect.size.x-1));
} }
} }
} }

@ -91,8 +91,8 @@ void State_GameRun::OnUserUpdate(Crawler*game){
} }
void State_GameRun::Draw(Crawler*game){ void State_GameRun::Draw(Crawler*game){
game->RenderHud(); game->RenderHud();
//FontTest(); //Enable to test font coloring. FontTest(); //Enable to test font coloring.
FontSpriteTest(); //Enable to test font coloring. //FontSpriteTest(); //Enable to test font coloring.
} }
void State_GameRun::FontTest(){ void State_GameRun::FontTest(){

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 2 #define VERSION_MINOR 2
#define VERSION_PATCH 1 #define VERSION_PATCH 1
#define VERSION_BUILD 4398 #define VERSION_BUILD 4434
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -116,9 +116,9 @@ 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 colorOverride=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(),const bool disableDynamicScaling=false);
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 colorOverride=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(),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); 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 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(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);
@ -628,108 +628,36 @@ 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 colorOverride){ 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){
static std::vector<PixelGameEngine::StringDecalData>letters; struct DecalData{
letters.clear(); Decal*decal;
bool wrappingOccurred=false; float expireTime=0.0f;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
Pixel textCol=col;
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
}; };
for (int skip=0,index=-1;auto c : sText) if(sText.length()==0)return;
{ static std::map<std::string,DecalData>garbageCollector;
index++; std::string key{sText};
if(skip){ if(!disableDynamicScaling){
skip--; key+=scale.str();
continue;
}
if(c==' '||c=='\t'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->GetFontDecal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
wrappingOccurred=false;
}
if(wrappingOccurred){
if(c!=' '&&c!='\t'){
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;
}
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->GetFontDecal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += 8.0f * scale.x;
}
letters.clear();
}
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 },colorOverride?col:textCol);
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=pge->GetWrappedTextSize(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal;
pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK);
pge->DrawString({0,0},sText,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr);
newDecal->Update();
} }
for(PixelGameEngine::StringDecalData&letter:letters){ garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
DrawPartialDecal(pos + drawingMarker, pge->GetFontDecal(), letter.sourcePos, letter.sourceSize, scale, letter.col); std::erase_if(garbageCollector,[&](auto&key){
drawingMarker.x += 8.0f * scale.x; if(key.second.expireTime<pge->GetRuntime()){
delete key.second.decal;
return true;
} }
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){
@ -754,115 +682,43 @@ void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::
DrawDecal(pos,garbageCollector[key].decal,scale/4,col); DrawDecal(pos,garbageCollector[key].decal,scale/4,col);
} }
void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool colorOverride){ 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){
static std::vector<PixelGameEngine::StringDecalData>letters; struct DecalData{
letters.clear(); Decal*decal;
bool wrappingOccurred=false; float expireTime=0.0f;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
Pixel textCol=col;
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
}; };
for (int skip=0,index=-1;auto c : sText) if(sText.length()==0)return;
{ static std::map<std::string,DecalData>garbageCollector;
index++; std::string key{sText};
if(skip){ if(!disableDynamicScaling){
skip--; key+=scale.str();
continue;
}
if(c==' '||c=='\t'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, pge->GetFontDecal(), 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'){
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;
}
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->GetFontDecal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
}
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 },colorOverride?col:textCol);
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=pge->GetWrappedTextSizeProp(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal;
pge->SetDrawTarget(newDecal->sprite);
pge->Clear(BLANK);
pge->DrawStringProp({0,0},sText,WHITE,1U,width/scale.x);
pge->SetDrawTarget(nullptr);
newDecal->Update();
} }
for(PixelGameEngine::StringDecalData&letter:letters){ garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
DrawPartialDecal(pos + drawingMarker, pge->GetFontDecal(), letter.sourcePos, letter.sourceSize, scale, letter.col); std::erase_if(garbageCollector,[&](auto&key){
drawingMarker.x += float(pge->vFontSpacing[letter.c - 32].y) * scale.x; if(key.second.expireTime<pge->GetRuntime()){
delete key.second.decal;
return true;
} }
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){ 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){
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
DrawStringDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width,true); DrawStringDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width);
} }
} }
} }
@ -873,7 +729,7 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
DrawStringPropDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width,true); DrawStringPropDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width);
} }
} }
} }

@ -1122,8 +1122,8 @@ namespace olc
void DrawShadowStringProp(const olc::vi2d& pos, std::string_view sText, 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 DrawShadowStringProp(const olc::vi2d& pos, std::string_view sText, 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);
olc::vi2d GetTextSize(std::string_view s); olc::vi2d GetTextSize(std::string_view s);
olc::vi2d GetTextSizeProp(std::string_view s); olc::vi2d GetTextSizeProp(std::string_view s);
olc::vi2d GetWrappedTextSize(std::string_view s,const float width=std::numeric_limits<int>::max(),const float scale=1); olc::vi2d GetWrappedTextSize(std::string_view s,const float width=std::numeric_limits<int>::max(),const vf2d scale={1,1});
olc::vi2d GetWrappedTextSizeProp(std::string_view s,const float width=std::numeric_limits<int>::max(),const float scale=1); olc::vi2d GetWrappedTextSizeProp(std::string_view s,const float width=std::numeric_limits<int>::max(),const vf2d scale={1,1});
void DrawString(const olc::vi2d& pos, std::string_view sText, Pixel col = olc::WHITE, uint32_t scale = 1,const float width=std::numeric_limits<float>::max(),const bool colorOverride=false); void DrawString(const olc::vi2d& pos, std::string_view sText, Pixel col = olc::WHITE, uint32_t scale = 1,const float width=std::numeric_limits<float>::max(),const bool colorOverride=false);
void DrawString(int32_t x, int32_t y, std::string_view sText, Pixel col = olc::WHITE, uint32_t scale = 1,const float width=std::numeric_limits<float>::max(),const bool colorOverride=false); void DrawString(int32_t x, int32_t y, std::string_view sText, Pixel col = olc::WHITE, uint32_t scale = 1,const float width=std::numeric_limits<float>::max(),const bool colorOverride=false);
@ -1149,9 +1149,9 @@ namespace olc
void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, const olc::vf2d& scale = { 1.0f,1.0f }, const olc::Pixel& tint = olc::WHITE); void DrawRotatedDecal(const olc::vf2d& pos, olc::Decal* decal, const float fAngle, const olc::vf2d& center = { 0.0f, 0.0f }, 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 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);
// 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 colorOverride=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(),const bool disableDynamicScaling=false);
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 colorOverride=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(),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); 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 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(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);
@ -2195,10 +2195,10 @@ namespace olc
{ return bHasInputFocus; } { return bHasInputFocus; }
HWButton PixelGameEngine::GetKey(Key k) const HWButton PixelGameEngine::GetKey(Key k) const
{ return pKeyboardState[k]; } { return IsFocused()?pKeyboardState[k]:HWButton{}; }
HWButton PixelGameEngine::GetMouse(uint32_t b) const HWButton PixelGameEngine::GetMouse(uint32_t b) const
{ return pMouseState[b]; } { return IsFocused()?pMouseState[b]:HWButton{}; }
int32_t PixelGameEngine::GetMouseX() const int32_t PixelGameEngine::GetMouseX() const
{ return vMousePos.x; } { return vMousePos.x; }
@ -2210,7 +2210,7 @@ namespace olc
{ return vMousePos; } { return vMousePos; }
int32_t PixelGameEngine::GetMouseWheel() const int32_t PixelGameEngine::GetMouseWheel() const
{ return nMouseWheelDelta; } { return IsFocused()?nMouseWheelDelta:0; }
int32_t PixelGameEngine::ScreenWidth() const int32_t PixelGameEngine::ScreenWidth() const
{ return vScreenSize.x; } { return vScreenSize.x; }
@ -3352,221 +3352,77 @@ namespace olc
void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint) void PixelGameEngine::DrawPartialWarpedDecal(olc::Decal* decal, const olc::vf2d(&pos)[4], const olc::vf2d& source_pos, const olc::vf2d& source_size, const olc::Pixel& tint)
{ DrawPartialWarpedDecal(decal, &pos[0], source_pos, source_size, tint); } { DrawPartialWarpedDecal(decal, &pos[0], source_pos, source_size, tint); }
void PixelGameEngine::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool colorOverride) void PixelGameEngine::DrawStringDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale,const float width,const bool disableDynamicScaling)
{ {
static std::vector<PixelGameEngine::StringDecalData>letters; struct DecalData{
letters.clear(); Decal*decal;
bool wrappingOccurred=false; float expireTime=0.0f;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
Pixel textCol=col;
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
}; };
for (int skip=0,index=-1;auto c : sText) if(sText.length()==0)return;
{ static std::map<std::string,DecalData>garbageCollector;
index++; std::string key{sText};
if(skip){ if(!disableDynamicScaling){
skip--; key+=scale.str();
continue;
}
if(c==' '||c=='\t'){
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'){
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;
}
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();
}
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 },colorOverride?col:textCol);
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=GetWrappedTextSize(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal;
SetDrawTarget(newDecal->sprite);
Clear(BLANK);
DrawString({0,0},sText,WHITE,1U,width/scale.x);
SetDrawTarget(nullptr);
newDecal->Update();
} }
for(PixelGameEngine::StringDecalData&letter:letters){ garbageCollector[key].expireTime=GetRuntime()+120.0f;
DrawPartialDecal(pos + drawingMarker, fontRenderable.Decal(), letter.sourcePos, letter.sourceSize, scale, letter.col); std::erase_if(garbageCollector,[&](auto&key){
drawingMarker.x += 8.0f * scale.x; if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
} }
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
void PixelGameEngine::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool colorOverride) void PixelGameEngine::DrawStringPropDecal(const olc::vf2d& pos, std::string_view sText, const Pixel col, const olc::vf2d& scale, const float width,const bool disableDynamicScaling)
{ {
static std::vector<PixelGameEngine::StringDecalData>letters; struct DecalData{
letters.clear(); Decal*decal;
bool wrappingOccurred=false; float expireTime=0.0f;
olc::vf2d planningMarker = { 0.0f, 0.0f };
olc::vf2d drawingMarker = { 0.0f, 0.0f };
Pixel textCol=col;
const auto hexToNumber=[](char c){
if(c<='9')return c-'0';
return (c-'A')+10;
}; };
for (int skip=0,index=-1;auto c : sText) if(sText.length()==0)return;
{ static std::map<std::string,DecalData>garbageCollector;
index++; std::string key{sText};
if(skip){ if(!disableDynamicScaling){
skip--; key+=scale.str();
continue;
}
if(c==' '||c=='\t'){
for(PixelGameEngine::StringDecalData&letter:letters){
DrawPartialDecal(pos + drawingMarker, GetFontDecal(), 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'){
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;
}
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, GetFontDecal(), letter.sourcePos, letter.sourceSize, scale, letter.col);
drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x;
}
letters.clear();
}
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 },colorOverride?col:textCol);
} }
if(!garbageCollector.count(key)){ //If the text key already exists, don't have to recreate the decal, just update the expire time.
vi2d imageSize=GetWrappedTextSizeProp(sText,width,scale);
Decal*newDecal=new Decal(new Sprite(imageSize.x/scale.x,imageSize.y/scale.x));
garbageCollector[key].decal=newDecal;
SetDrawTarget(newDecal->sprite);
Clear(BLANK);
DrawStringProp({0,0},sText,WHITE,1U,width/scale.x);
SetDrawTarget(nullptr);
newDecal->Update();
} }
for(PixelGameEngine::StringDecalData&letter:letters){ garbageCollector[key].expireTime=GetRuntime()+120.0f;
DrawPartialDecal(pos + drawingMarker, GetFontDecal(), letter.sourcePos, letter.sourceSize, scale, letter.col); std::erase_if(garbageCollector,[&](auto&key){
drawingMarker.x += float(vFontSpacing[letter.c - 32].y) * scale.x; if(key.second.expireTime<GetRuntime()){
delete key.second.decal;
return true;
} }
return false;
});
DrawDecal(pos,garbageCollector[key].decal,scale,col);
} }
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){ 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){
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
DrawStringDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width,true); DrawStringDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width);
} }
} }
} }
@ -3653,7 +3509,7 @@ namespace olc
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){ for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){ for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){
if(x!=0||y!=0){ if(x!=0||y!=0){
DrawStringPropDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width,true); DrawStringPropDecal(pos+vf2d{x,y}, sText, shadowCol,scale,width);
} }
} }
} }
@ -3750,9 +3606,12 @@ namespace olc
DrawRotatedStringPropDecal(pos, sText,fAngle,center,col,scale); DrawRotatedStringPropDecal(pos, sText,fAngle,center,col,scale);
} }
olc::vi2d PixelGameEngine::GetWrappedTextSize(std::string_view s,const float width,const float scale) olc::vi2d PixelGameEngine::GetWrappedTextSize(std::string_view s,const float width,const vf2d scale)
{ {
float adjustedWidth=width/scale; float adjustedWidth=width;
if(width!=std::numeric_limits<float>::max()){
adjustedWidth/=scale.x;
}
int lettersWidth=0; int lettersWidth=0;
float maxWidth=0; float maxWidth=0;
bool wrappingOccurred=false; bool wrappingOccurred=false;
@ -3819,6 +3678,7 @@ namespace olc
} }
} }
drawingMarker.x += lettersWidth; drawingMarker.x += lettersWidth;
maxWidth=std::max(maxWidth,drawingMarker.x);
return vi2d(vf2d{maxWidth,planningMarker.y+8}*scale); return vi2d(vf2d{maxWidth,planningMarker.y+8}*scale);
} }
@ -3993,9 +3853,12 @@ namespace olc
return size; return size;
} }
olc::vi2d PixelGameEngine::GetWrappedTextSizeProp(std::string_view s,const float width,const float scale) olc::vi2d PixelGameEngine::GetWrappedTextSizeProp(std::string_view s,const float width,const vf2d scale)
{ {
float adjustedWidth=width/scale; float adjustedWidth=width;
if(width!=std::numeric_limits<float>::max()){
adjustedWidth/=scale.x;
}
int lettersWidth=0; int lettersWidth=0;
float maxWidth=0; float maxWidth=0;
bool wrappingOccurred=false; bool wrappingOccurred=false;

Binary file not shown.
Loading…
Cancel
Save