GTK: make OSD scalable (#769)

* gtk: make OSD scalable

* Scale save slot indicator (oops), make text outlines look smoother, use
larger font when not scaling

* Save and load HUD layout, prefer raster font on low resolution, select
vector font size close to raster one, make OSDCLASS::scale floating point

* Build fix

* Add reset HUD layout action, only require fontconfig if libagg is found.

* Try another font in case we could not locate monospace

* Detect screen bytes per pixel instead of hardcoding it, define
AGG2D_USE_VECTORFONTS if fontconfig is found.

* Different pixel formats are handled by different draw target
implementations
This commit is contained in:
Max Fedotov 2024-02-14 11:20:57 +03:00 committed by GitHub
parent 6508c2b115
commit 45738beb88
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 470 additions and 121 deletions

View File

@ -1370,6 +1370,17 @@ AGG2D_TEMPLATE void TAGG2D::text(double x, double y, const wchar_t* str, unsigne
}
}
AGG2D_TEMPLATE double TAGG2D::textWidth(const char* str)
{
return textWidth(str, (unsigned int)strlen(str));
}
AGG2D_TEMPLATE void TAGG2D::text(double x, double y, const char* str, bool roundOff, double dx, double dy)
{
text(x, y, str, (unsigned int)strlen(str), roundOff, dx, dy);
}
#endif
//------------------------------------------------------------------------
@ -1945,11 +1956,11 @@ AGG2D_TEMPLATE void TAGG2D::render(FontRasterizer& ras, FontScanline& sl)
{
if(m_blendMode == BlendAlpha)
{
Agg2DRenderer::render(*this, m_renBase, m_renSolid, ras, sl);
Agg2DRenderer<PixFormatSet, PixFormatSet>::render(*this, m_renBase, m_renSolid, ras, sl);
}
else
{
Agg2DRenderer::render(*this, m_renBaseComp, m_renSolidComp, ras, sl);
Agg2DRenderer<PixFormatSet, PixFormatSet>::render(*this, m_renBaseComp, m_renSolidComp, ras, sl);
}
}

View File

@ -50,13 +50,23 @@ static s64 hudTimer;
static void SetHudDummy (HudCoordinates *hud)
{
hud->x=666;
hud->y=666;
hud->x=-666;
hud->y=-666;
}
static bool IsHudDummy (HudCoordinates *hud)
{
return (hud->x == 666 && hud->y == 666);
return (hud->x == -666 && hud->y == -666);
}
static int ScreenWidth()
{
return 256*osd->scale;
}
static int ScreenHeight()
{
return 192*osd->scale;
}
template<typename T>
@ -64,21 +74,47 @@ static T calcY(T y) // alters a GUI element y coordinate as necessary to obey sw
{
if(osd->singleScreen)
{
if(y >= 192)
y -= 192;
if(y >= ScreenHeight())
y -= ScreenHeight();
if(osd->swapScreens)
y += 192;
y += ScreenHeight();
}
else if(osd->swapScreens)
{
if(y >= 192)
y -= 192;
if(y >= ScreenHeight())
y -= ScreenHeight();
else
y += 192;
y += ScreenHeight();
}
return y;
}
static void RenderTextAutoVector(double x, double y, const std::string& str, bool shadow = true, double shadowOffset = 1.0)
{
#ifdef AGG2D_USE_VECTORFONTS
bool render_vect = false;
if(osd)
if(osd->useVectorFonts)
render_vect = true;
if(render_vect)
{
if(shadow)
aggDraw.hud->renderVectorFontTextDropshadowed(x, y, str, shadowOffset);
else
aggDraw.hud->renderVectorFontText(x, y, str);
}
else
{
#endif
if(shadow)
aggDraw.hud->renderTextDropshadowed(x, y, str);
else
aggDraw.hud->renderText(x, y, str);
#ifdef AGG2D_USE_VECTORFONTS
}
#endif
}
void EditHud(s32 x, s32 y, HudStruct *hudstruct) {
u32 i = 0;
@ -108,8 +144,10 @@ void EditHud(s32 x, s32 y, HudStruct *hudstruct) {
//sanity checks
if(hud.x < 0) hud.x = 0;
if(hud.y < 0) hud.y = 0;
if(hud.x > 245)hud.x = 245; //margins
if(hud.y > 384-16)hud.y = 384-16;
if(hud.x > ScreenWidth()-11*osd->scale)
hud.x = ScreenWidth()-11*osd->scale; //margins
if(hud.y > ScreenHeight()*2-16*osd->scale)
hud.y = ScreenHeight()*2-16*osd->scale;
if(hud.clicked)
{
@ -136,45 +174,47 @@ void HudClickRelease(HudStruct *hudstruct) {
void HudStruct::reset()
{
double sc=(osd ? osd->scale : 1.0);
FpsDisplay.x=0;
FpsDisplay.y=5;
FpsDisplay.xsize=166;
FpsDisplay.ysize=10;
FpsDisplay.y=5*sc;
FpsDisplay.xsize=166*sc;
FpsDisplay.ysize=10*sc;
FrameCounter.x=0;
FrameCounter.y=25;
FrameCounter.xsize=60;
FrameCounter.ysize=10;
FrameCounter.y=25*sc;
FrameCounter.xsize=60*sc;
FrameCounter.ysize=10*sc;
InputDisplay.x=0;
InputDisplay.y=45;
InputDisplay.xsize=220;
InputDisplay.ysize=10;
InputDisplay.y=45*sc;
InputDisplay.xsize=220*sc;
InputDisplay.ysize=10*sc;
GraphicalInputDisplay.x=8;
GraphicalInputDisplay.y=328;
GraphicalInputDisplay.xsize=102;
GraphicalInputDisplay.ysize=50;
GraphicalInputDisplay.x=8*sc;
GraphicalInputDisplay.y=328*sc;
GraphicalInputDisplay.xsize=102*sc;
GraphicalInputDisplay.ysize=50*sc;
LagFrameCounter.x=0;
LagFrameCounter.y=65;
LagFrameCounter.xsize=30;
LagFrameCounter.ysize=10;
LagFrameCounter.y=65*sc;
LagFrameCounter.xsize=30*sc;
LagFrameCounter.ysize=10*sc;
Microphone.x=0;
Microphone.y=85;
Microphone.xsize=20;
Microphone.ysize=10;
Microphone.y=85*sc;
Microphone.xsize=20*sc;
Microphone.ysize=10*sc;
RTCDisplay.x=0;
RTCDisplay.y=105;
RTCDisplay.xsize=220;
RTCDisplay.ysize=10;
RTCDisplay.y=105*sc;
RTCDisplay.xsize=220*sc;
RTCDisplay.ysize=10*sc;
SavestateSlots.x = 8;
SavestateSlots.y = 160;
SavestateSlots.xsize = 240;
SavestateSlots.ysize = 24;
SavestateSlots.x = 8*sc;
SavestateSlots.y = 160*sc;
SavestateSlots.xsize = 240*sc;
SavestateSlots.ysize = 24*sc;
#ifdef _MSC_VER
#define AGG_OSD_SETTING(which,comp) which.comp = GetPrivateProfileInt("HudEdit", #which "." #comp, which.comp, IniName);
@ -186,6 +226,51 @@ void HudStruct::reset()
clicked = false;
}
void HudStruct::rescale(double oldScale, double newScale)
{
double sc=newScale/oldScale;
FpsDisplay.x*=sc;
FpsDisplay.y*=sc;
FpsDisplay.xsize*=sc;
FpsDisplay.ysize*=sc;
FrameCounter.x*=sc;
FrameCounter.y*=sc;
FrameCounter.xsize*=sc;
FrameCounter.ysize*=sc;
InputDisplay.x*=sc;
InputDisplay.y*=sc;
InputDisplay.xsize*=sc;
InputDisplay.ysize*=sc;
GraphicalInputDisplay.x*=sc;
GraphicalInputDisplay.y*=sc;
GraphicalInputDisplay.xsize*=sc;
GraphicalInputDisplay.ysize*=sc;
LagFrameCounter.x*=sc;
LagFrameCounter.y*=sc;
LagFrameCounter.xsize*=sc;
LagFrameCounter.ysize*=sc;
Microphone.x*=sc;
Microphone.y*=sc;
Microphone.xsize*=sc;
Microphone.ysize*=sc;
RTCDisplay.x*=sc;
RTCDisplay.y*=sc;
RTCDisplay.xsize*=sc;
RTCDisplay.ysize*=sc;
SavestateSlots.x*=sc;
SavestateSlots.y*=sc;
SavestateSlots.xsize*=sc;
SavestateSlots.ysize*=sc;
}
static void joyFill(int n) {
bool pressedForGame = NDS_getFinalUserInput().buttons.array[n];
@ -235,8 +320,8 @@ static void drawPad(double x, double y, double ratio) {
// aligning to odd half-pixel boundaries prevents agg2d from blurring thin straight lines
x = floor(x) + 0.5;
y = floor(calcY(y)) + 0.5;
double xc = 41 - 0.5;
double yc = 20 - 0.5;
double xc = 41*osd->scale - 0.5;
double yc = 20*osd->scale - 0.5;
aggDraw.hud->lineColor(128,128,128,255);
@ -252,12 +337,12 @@ static void drawPad(double x, double y, double ratio) {
aggDraw.hud->roundedRect (screenLeft, screenTop, screenRight, screenBottom, 1);
joyEllipse(.89,.45,xc,yc,x,y,ratio,1,6);//B
joyEllipse(.89,.22,xc,yc,x,y,ratio,1,3);//X
joyEllipse(.83,.34,xc,yc,x,y,ratio,1,4);//Y
joyEllipse(.95,.34,xc,yc,x,y,ratio,1,5);//A
joyEllipse(.82,.716,xc,yc,x,y,ratio,.5,7);//Start
joyEllipse(.82,.842,xc,yc,x,y,ratio,.5,8);//Select
joyEllipse(.89,.45,xc,yc,x,y,ratio,osd->scale,6);//B
joyEllipse(.89,.22,xc,yc,x,y,ratio,osd->scale,3);//X
joyEllipse(.83,.34,xc,yc,x,y,ratio,osd->scale,4);//Y
joyEllipse(.95,.34,xc,yc,x,y,ratio,osd->scale,5);//A
joyEllipse(.82,.716,xc,yc,x,y,ratio,osd->scale * .5,7);//Start
joyEllipse(.82,.842,xc,yc,x,y,ratio,osd->scale * .5,8);//Select
double dpadPoints [][2] = {
@ -311,29 +396,29 @@ static void drawPad(double x, double y, double ratio) {
// touch pad
{
BOOL gameTouchOn = nds.isTouch;
double gameTouchX = screenLeft+1 + (nds.scr_touchX * 0.0625) * (screenRight - screenLeft - 2) / 256.0;
double gameTouchY = screenTop+1 + (nds.scr_touchY * 0.0625) * (screenBottom - screenTop - 2) / 192.0;
double gameTouchX = screenLeft+1 + (nds.scr_touchX * osd->scale * 0.0625) * (screenRight - screenLeft - 2) / (double)ScreenWidth();
double gameTouchY = screenTop+1 + (nds.scr_touchY * osd->scale * 0.0625) * (screenBottom - screenTop - 2) / (double)ScreenHeight();
bool physicalTouchOn = NDS_getRawUserInput().touch.isTouch;
double physicalTouchX = screenLeft+1 + (NDS_getRawUserInput().touch.touchX * 0.0625) * (screenRight - screenLeft - 2) / 256.0;
double physicalTouchY = screenTop+1 + (NDS_getRawUserInput().touch.touchY * 0.0625) * (screenBottom - screenTop - 2) / 192.0;
double physicalTouchX = screenLeft+1 + (NDS_getRawUserInput().touch.touchX * osd->scale * 0.0625) * (screenRight - screenLeft - 2) / (double)ScreenWidth();
double physicalTouchY = screenTop+1 + (NDS_getRawUserInput().touch.touchY * osd->scale * 0.0625) * (screenBottom - screenTop - 2) / (double)ScreenHeight();
if(gameTouchOn && physicalTouchOn && gameTouchX == physicalTouchX && gameTouchY == physicalTouchY)
{
aggDraw.hud->fillColor(0,0,0,255);
aggDraw.hud->ellipse(gameTouchX, gameTouchY, ratio*0.37, ratio*0.37);
aggDraw.hud->ellipse(gameTouchX, gameTouchY, osd->scale*ratio*0.37, osd->scale*ratio*0.37);
}
else
{
if(physicalTouchOn)
{
aggDraw.hud->fillColor(0,0,0,128);
aggDraw.hud->ellipse(physicalTouchX, physicalTouchY, ratio*0.5, ratio*0.5);
aggDraw.hud->ellipse(physicalTouchX, physicalTouchY, osd->scale*ratio*0.5, osd->scale*ratio*0.5);
aggDraw.hud->fillColor(0,255,0,255);
aggDraw.hud->ellipse(physicalTouchX, physicalTouchY, ratio*0.37, ratio*0.37);
aggDraw.hud->ellipse(physicalTouchX, physicalTouchY, osd->scale*ratio*0.37, osd->scale*ratio*0.37);
}
if(gameTouchOn)
{
aggDraw.hud->fillColor(255,0,0,255);
aggDraw.hud->ellipse(gameTouchX, gameTouchY, ratio*0.37, ratio*0.37);
aggDraw.hud->ellipse(gameTouchX, gameTouchY, osd->scale*ratio*0.37, osd->scale*ratio*0.37);
}
}
}
@ -379,8 +464,8 @@ static void TextualInputDisplay() {
// cast from char to std::string is a bit awkward
std::string str(buttonChars+i, 2);
str[1] = '\0';
aggDraw.hud->renderTextDropshadowed(x, calcY(Hud.InputDisplay.y), str);
RenderTextAutoVector(x, calcY(Hud.InputDisplay.y), str, true, osd ? osd->scale : 1);
}
// touch pad
@ -396,7 +481,7 @@ static void TextualInputDisplay() {
{
sprintf(str, "%d,%d", gameTouchX, gameTouchY);
aggDraw.hud->lineColor(255,255,255,255);
aggDraw.hud->renderTextDropshadowed(x, calcY(Hud.InputDisplay.y), str);
RenderTextAutoVector(x, calcY(Hud.InputDisplay.y), str, true, osd ? osd->scale : 1);
}
else
{
@ -404,13 +489,13 @@ static void TextualInputDisplay() {
{
sprintf(str, "%d,%d", gameTouchX, gameTouchY);
aggDraw.hud->lineColor(255,48,48,255);
aggDraw.hud->renderTextDropshadowed(x, calcY(Hud.InputDisplay.y)-(physicalTouchOn?8:0), str);
RenderTextAutoVector(x, calcY(Hud.InputDisplay.y)-(physicalTouchOn?8:0), str, true, osd ? osd->scale : 1);
}
if(physicalTouchOn)
{
sprintf(str, "%d,%d", physicalTouchX, physicalTouchY);
aggDraw.hud->lineColor(0,192,0,255);
aggDraw.hud->renderTextDropshadowed(x, calcY(Hud.InputDisplay.y)+(gameTouchOn?8:0), str);
RenderTextAutoVector(x, calcY(Hud.InputDisplay.y)+(gameTouchOn?8:0), str, true, osd ? osd->scale : 1);
}
}
}
@ -418,10 +503,10 @@ static void TextualInputDisplay() {
static void OSD_HandleTouchDisplay() {
// note: calcY should not be used in this function.
aggDraw.hud->lineWidth(1.0);
aggDraw.hud->lineWidth(osd->scale);
temptouch.X = NDS_getRawUserInput().touch.touchX >> 4;
temptouch.Y = NDS_getRawUserInput().touch.touchY >> 4;
temptouch.X = (NDS_getRawUserInput().touch.touchX >> 4) * osd->scale;
temptouch.Y = (NDS_getRawUserInput().touch.touchY >> 4) * osd->scale;
if(touchshadow) {
@ -432,27 +517,27 @@ static void OSD_HandleTouchDisplay() {
temptouch = touch[i];
if(temptouch.X != 0 || temptouch.Y != 0) {
aggDraw.hud->lineColor(0, 255, 0, touchalpha[i]);
aggDraw.hud->line(temptouch.X - 256, temptouch.Y + 192, temptouch.X + 256, temptouch.Y + 192); //horiz
aggDraw.hud->line(temptouch.X, temptouch.Y - 256, temptouch.X, temptouch.Y + 384); //vert
aggDraw.hud->line(temptouch.X - ScreenWidth(), temptouch.Y + ScreenHeight(), temptouch.X + ScreenWidth(), temptouch.Y + ScreenHeight()); //horiz
aggDraw.hud->line(temptouch.X, temptouch.Y - ScreenWidth(), temptouch.X, temptouch.Y + ScreenHeight()*2); //vert
aggDraw.hud->fillColor(0, 0, 0, touchalpha[i]);
aggDraw.hud->rectangle(temptouch.X-1, temptouch.Y + 192-1, temptouch.X+1, temptouch.Y + 192+1);
aggDraw.hud->rectangle(temptouch.X-1, temptouch.Y + ScreenHeight()-1, temptouch.X+1, temptouch.Y + ScreenHeight()+1);
}
}
}
else
if(NDS_getRawUserInput().touch.isTouch) {
aggDraw.hud->lineColor(0, 255, 0, 128);
aggDraw.hud->line(temptouch.X - 256, temptouch.Y + 192, temptouch.X + 256, temptouch.Y + 192); //horiz
aggDraw.hud->line(temptouch.X, temptouch.Y - 256, temptouch.X, temptouch.Y + 384); //vert
aggDraw.hud->line(temptouch.X - ScreenWidth(), temptouch.Y + ScreenHeight(), temptouch.X + ScreenWidth(), temptouch.Y + ScreenHeight()); //horiz
aggDraw.hud->line(temptouch.X, temptouch.Y - ScreenWidth(), temptouch.X, temptouch.Y + ScreenHeight()*2); //vert
}
if(nds.isTouch)
{
temptouch.X = nds.scr_touchX / 16;
temptouch.Y = nds.scr_touchY / 16;
temptouch.X = nds.scr_touchX / 16 * osd->scale;
temptouch.Y = nds.scr_touchY / 16 * osd->scale;
aggDraw.hud->lineColor(255, 0, 0, 128);
aggDraw.hud->line(temptouch.X - 256, temptouch.Y + 192, temptouch.X + 256, temptouch.Y + 192); //horiz
aggDraw.hud->line(temptouch.X, temptouch.Y - 256, temptouch.X, temptouch.Y + 384); //vert
aggDraw.hud->line(temptouch.X - ScreenWidth(), temptouch.Y + ScreenHeight(), temptouch.X + ScreenWidth(), temptouch.Y + ScreenHeight()); //horiz
aggDraw.hud->line(temptouch.X, temptouch.Y - ScreenWidth(), temptouch.X, temptouch.Y + ScreenHeight()*2); //vert
}
}
@ -476,24 +561,24 @@ static void DrawStateSlots(){
if(alpha!=0)
{
aggDraw.hud->lineWidth(1.0);
aggDraw.hud->lineWidth(osd->scale);
aggDraw.hud->lineColor(0, 0, 0, alpha);
aggDraw.hud->fillColor(255, 255, 255, alpha);
for ( int i = 0, xpos=0; i < 10; xpos=xpos+24) {
for ( int i = 0, xpos=0; i < 10; xpos=xpos+24*osd->scale) {
int yheight=0;
aggDraw.hud->fillLinearGradient(xloc + xpos, yloc - yheight, xloc + 22 + xpos, yloc + 20 + yheight+20, agg::rgba8(100,200,255,alpha), agg::rgba8(255,255,255,0));
aggDraw.hud->fillLinearGradient(xloc + xpos, yloc - yheight, xloc + 22*osd->scale + xpos, yloc + 20*osd->scale + yheight+20*osd->scale, agg::rgba8(100,200,255,alpha), agg::rgba8(255,255,255,0));
if(lastSaveState == i) {
yheight = 5;
aggDraw.hud->fillLinearGradient(xloc + xpos, yloc - yheight, 22 + xloc + xpos, yloc + 20 + yheight+20, agg::rgba8(100,255,255,alpha), agg::rgba8(255,255,255,0));
yheight = 5*osd->scale;
aggDraw.hud->fillLinearGradient(xloc + xpos, yloc - yheight, 22*osd->scale + xloc + xpos, yloc + 20*osd->scale + yheight+20*osd->scale, agg::rgba8(100,255,255,alpha), agg::rgba8(255,255,255,0));
}
aggDraw.hud->rectangle(xloc + xpos , yloc - yheight, xloc + 22 + xpos , yloc + 20 + yheight);
aggDraw.hud->rectangle(xloc + xpos , yloc - yheight, xloc + 22*osd->scale + xpos , yloc + 20*osd->scale + yheight);
snprintf(number, 10, "%d", i);
aggDraw.hud->renderText(xloc + 1 + xpos + 4, yloc+4, std::string(number));
RenderTextAutoVector(xloc + osd->scale + xpos + 4*osd->scale, yloc+4*osd->scale, std::string(number), true, osd->scale);
i++;
}
}
@ -511,10 +596,10 @@ static void DrawEditableElementIndicators()
HudCoordinates &hud = Hud.hud(i);
aggDraw.hud->fillColor(0,0,0,0);
aggDraw.hud->lineColor(0,0,0,64);
aggDraw.hud->lineWidth(2.0);
aggDraw.hud->lineWidth(2.0*osd->scale);
aggDraw.hud->rectangle(hud.x,calcY(hud.y),hud.x+hud.xsize+1.0,calcY(hud.y)+hud.ysize+1.0);
aggDraw.hud->lineColor(255,hud.clicked?127:255,0,255);
aggDraw.hud->lineWidth(1.0);
aggDraw.hud->lineWidth(osd->scale);
aggDraw.hud->rectangle(hud.x-0.5,calcY(hud.y)-0.5,hud.x+hud.xsize+0.5,calcY(hud.y)+hud.ysize+0.5);
i++;
}
@ -624,9 +709,11 @@ OSDCLASS::OSDCLASS(u8 core)
singleScreen = false;
swapScreens = false;
scale = 1.0;
needUpdate = false;
#ifdef AGG2D_USE_VECTORFONTS
useVectorFonts = false;
#endif
if (core==0)
strcpy(name,"Core A");
else
@ -707,7 +794,7 @@ void OSDCLASS::update()
for (int i=0; i < lastLineText; i++)
{
aggDraw.hud->lineColor(lineColor[i]);
aggDraw.hud->renderTextDropshadowed(lineText_x,lineText_y+(i*16),lineText[i]);
RenderTextAutoVector(lineText_x, lineText_y+(i*16), lineText[i], true, osd->scale);
}
}
else
@ -789,7 +876,7 @@ void OSDCLASS::addFixed(u16 x, u16 y, const char *fmt, ...)
va_end(list);
aggDraw.hud->lineColor(255,255,255);
aggDraw.hud->renderTextDropshadowed(x,calcY(y),msg);
RenderTextAutoVector(x, calcY(y), msg, true, osd->scale);
needUpdate = true;
}

View File

@ -73,6 +73,7 @@ public:
HudCoordinates &hud(int i) { return ((HudCoordinates*)this)[i]; }
void reset();
void rescale(double oldScale, double newScale);
int fps, fps3d, cpuload[2], cpuloopIterationCount;
char rtcString[25];
@ -92,7 +93,7 @@ class OSDCLASS
private:
u64 offset;
u8 mode;
u16 rotAngle;
u16 lineText_x;
@ -111,6 +112,10 @@ public:
char name[7]; // for debuging
bool singleScreen;
bool swapScreens;
double scale;
#ifdef AGG2D_USE_VECTORFONTS
bool useVectorFonts;
#endif
OSDCLASS(u8 core);
~OSDCLASS();
@ -125,7 +130,7 @@ public:
void addLine(const char* fmt, va_list args);
void addFixed(u16 x, u16 y, const char *fmt, ...);
void border(bool enabled);
void SaveHudEditor();
};

View File

@ -118,20 +118,17 @@ static void Agg_init_fonts()
AggDraw_Desmume aggDraw;
#if defined(WIN32) || defined(HOST_LINUX)
T_AGG_RGBA agg_targetScreen(0, 256, 384, 1024);
#else
T_AGG_RGB555 agg_targetScreen(0, 256, 384, 1512);
#endif
T_AGG_RGBA agg_targetScreen_32bit(0, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT*2, GPU_FRAMEBUFFER_NATIVE_WIDTH*4);
T_AGG_RGB555 agg_targetScreen_16bit(0, GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT*2, GPU_FRAMEBUFFER_NATIVE_WIDTH*2);
static u32 luaBuffer[256*192*2];
T_AGG_RGBA agg_targetLua((u8*)luaBuffer, 256, 384, 1024);
static std::vector<u32> luaBuffer(GPU_FRAMEBUFFER_NATIVE_WIDTH*GPU_FRAMEBUFFER_NATIVE_HEIGHT*2);
T_AGG_RGBA agg_targetLua((u8*)luaBuffer.data(), GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT*2, GPU_FRAMEBUFFER_NATIVE_WIDTH*4);
static u32 hudBuffer[256*192*2];
T_AGG_RGBA agg_targetHud((u8*)hudBuffer, 256, 384, 1024);
static std::vector<u32> hudBuffer(GPU_FRAMEBUFFER_NATIVE_WIDTH*GPU_FRAMEBUFFER_NATIVE_HEIGHT*2);
T_AGG_RGBA agg_targetHud((u8*)hudBuffer.data(), GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT*2, GPU_FRAMEBUFFER_NATIVE_WIDTH*4);
static AggDrawTarget* targets[] = {
&agg_targetScreen,
&agg_targetScreen_32bit,
&agg_targetHud,
&agg_targetLua,
};
@ -139,6 +136,16 @@ static AggDrawTarget* targets[] = {
void Agg_init()
{
Agg_init_fonts();
switch(aggDraw.screenBytesPerPixel)
{
case 2:
targets[0]=&agg_targetScreen_16bit;
break;
case 4:
targets[0]=&agg_targetScreen_32bit;
break;
}
aggDraw.screen = targets[0];
aggDraw.hud = targets[1];
aggDraw.lua = targets[2];
@ -149,19 +156,35 @@ void Agg_init()
//and the more clever compositing isnt supported in non-windows
#ifdef WIN32
if(CommonSettings.single_core())
aggDraw.hud = &agg_targetScreen;
aggDraw.hud = aggDraw.screen;
#else
aggDraw.hud = &agg_targetScreen;
aggDraw.hud = aggDraw.screen;
#endif
aggDraw.hud->setFont("verdana18_bold");
}
AggDraw_Desmume::AggDraw_Desmume()
{
screenBytesPerPixel = 4;
}
void AggDraw_Desmume::setTarget(AggTarget newTarget)
{
target = targets[newTarget];
}
void Agg_setCustomSize(int w, int h)
{
hudBuffer.resize(w*h);
luaBuffer.resize(w*h);
if(aggDraw.screen)
aggDraw.screen->setDrawTargetDims(0, w, h, w*aggDraw.screenBytesPerPixel);
if(aggDraw.hud)
aggDraw.hud->setDrawTargetDims((u8*)hudBuffer.data(), w, h, w*4);
if(aggDraw.lua)
aggDraw.lua->setDrawTargetDims((u8*)luaBuffer.data(), w, h, w*4);
}
////temporary, just for testing the lib

View File

@ -19,7 +19,7 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
THE SOFTWARE.
*/
@ -43,18 +43,18 @@ THE SOFTWARE.
#include "agg_pixfmt_rgb.h"
#include "agg_pixfmt_rgba.h"
#include "agg_pixfmt_rgb_packed.h"
#include "agg_pixfmt_rgb_packed.h"
#include "agg2d.h"
typedef agg::rgba8 AggColor;
namespace agg
{
//NOTE - these blenders are necessary to change the rgb order from the defaults, which are incorrect for us
//this custom blender does more correct blending math than the default
//which is necessary or else drawing transparent pixels on (31,31,31) will yield (30,30,30)
namespace agg
{
//NOTE - these blenders are necessary to change the rgb order from the defaults, which are incorrect for us
//this custom blender does more correct blending math than the default
//which is necessary or else drawing transparent pixels on (31,31,31) will yield (30,30,30)
struct my_blender_rgb555_pre
{
typedef rgba8 color_type;
@ -242,6 +242,8 @@ public:
bool empty;
virtual void clear() = 0;
virtual void setDrawTargetDims(agg::int8u* buf, int width, int height, int stride) = 0;
virtual agg::rendering_buffer & buf() = 0;
@ -408,6 +410,9 @@ public:
virtual Agg2DBase::ImageResample imageResample() = 0;
static const agg::int8u* lookupFont(const std::string& name);
virtual void setFont(const std::string& name) = 0;
#ifdef AGG2D_USE_VECTORFONTS
virtual void setVectorFont(const std::string& fileName, int size, bool bold) = 0;
#endif
virtual void renderText(double dstX, double dstY, const std::string& str) = 0;
virtual void renderTextDropshadowed(double dstX, double dstY, const std::string& str)
{
@ -427,6 +432,30 @@ public:
lineColor(lineColorOld);
renderText(dstX,dstY,str);
}
#ifdef AGG2D_USE_VECTORFONTS
virtual void renderVectorFontText(double dstX, double dstY, const std::string& str) = 0;
virtual void renderVectorFontTextDropshadowed(double dstX, double dstY, const std::string& str, double shadowOffset = 1.0)
{
shadowOffset*=0.5;
AggColor lineColorOld = lineColor();
if(lineColorOld.r+lineColorOld.g+lineColorOld.b<192)
lineColor(255-lineColorOld.r,255-lineColorOld.g,255-lineColorOld.b);
else
lineColor(0,0,0);
fillColor(lineColor());
renderVectorFontText(dstX-shadowOffset,dstY-shadowOffset,str);
renderVectorFontText(dstX,dstY-shadowOffset,str);
renderVectorFontText(dstX+shadowOffset,dstY-shadowOffset,str);
renderVectorFontText(dstX-shadowOffset,dstY,str);
renderVectorFontText(dstX+shadowOffset,dstY,str);
renderVectorFontText(dstX-shadowOffset,dstY+shadowOffset,str);
renderVectorFontText(dstX,dstY+shadowOffset,str);
renderVectorFontText(dstX+shadowOffset,dstY+shadowOffset,str);
lineColor(lineColorOld);
fillColor(lineColor());
renderVectorFontText(dstX,dstY,str);
}
#endif
// Auxiliary
@ -458,6 +487,13 @@ public:
undirty();
}
}
virtual void setDrawTargetDims(agg::int8u* buf, int width, int height, int stride)
{
BASE::attach(buf,width,height,stride);
BASE::viewport(0, 0, width-1, height-1, 0, 0, width-1, height-1, TAGG2D::Anisotropic);
}
virtual agg::rendering_buffer & buf() { dirty(); return BASE::buf(); } // buf() might not always require calling dirty()
typename BASE::MyImage image() { return BASE::MyImage(buf()); }
@ -578,6 +614,9 @@ public:
virtual void polyline(double* xy, int numPoints) {dirty(); BASE::polyline(xy, numPoints);};
virtual void setFont(const std::string& name) { BASE::font(lookupFont(name)); }
#ifdef AGG2D_USE_VECTORFONTS
virtual void setVectorFont(const std::string& fileName, int size, bool bold) {BASE::font(fileName.c_str(), size, bold); }
#endif
virtual void renderText(double dstX, double dstY, const std::string& str) {
dirty();
int height = BASE::font()[0];
@ -585,6 +624,13 @@ public:
int offset = height-base*2;
BASE::renderText(dstX, dstY + offset, str);
}
#ifdef AGG2D_USE_VECTORFONTS
virtual void renderVectorFontText(double dstX, double dstY, const std::string& str) {
dirty();
BASE::flipText(true);
BASE::text(dstX, dstY + BASE::fontHeight(), str.c_str());
}
#endif
// Path commands
virtual void resetPath() {BASE::resetPath();};
@ -659,15 +705,18 @@ enum AggTarget
class AggDraw_Desmume : public AggDraw
{
public:
AggDraw_Desmume();
void setTarget(AggTarget newTarget);
//void composite(void* dest);
AggDrawTarget *screen, *hud, *lua;
int screenBytesPerPixel;
};
extern AggDraw_Desmume aggDraw;
void Agg_init();
void Agg_setCustomSize(int w, int h);
struct font_type
{

View File

@ -97,6 +97,25 @@ void value<string>::save() {
g_key_file_set_string(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), this->mData.c_str());
}
/* class value<vector<int> > */
template<>
void value<vector<int> >::load() {
gsize l;
int* val = g_key_file_get_integer_list(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), &l, NULL);
if(val)
{
this->mData.resize(l);
std::copy(val, val+l, this->mData.begin());
g_free(val);
}
}
template<>
void value<vector<int> >::save() {
g_key_file_set_integer_list(this->mKeyFile, this->mSection.c_str(), this->mKey.c_str(), this->mData.data(), this->mData.size());
}
/* class Config */
Config::Config()

View File

@ -49,6 +49,7 @@ OPT(hud_input, bool, false, HudDisplay, Input)
OPT(hud_graphicalInput, bool, false, HudDisplay, GraphicalInput)
OPT(hud_rtc, bool, false, HudDisplay, RTC)
OPT(hud_mic, bool, false, HudDisplay, Mic)
OPT(hud_layout, std::vector<int>, std::vector<int>(), HudDisplay, Layout)
/* Config */
OPT(fpslimiter, bool, true, Config, FpsLimiter)

View File

@ -31,6 +31,9 @@
#include <SDL.h>
#include <X11/Xlib.h>
#include <vector>
#ifdef AGG2D_USE_VECTORFONTS
#include <fontconfig/fontconfig.h>
#endif
#include "types.h"
#include "firmware.h"
@ -127,6 +130,15 @@ enum {
SUB_OBJ
};
#ifdef AGG2D_USE_VECTORFONTS
#define VECTOR_FONT_BASE_SIZE 16
static FcConfig* fontConfig;
static std::string vectorFontFile;
static std::string FindFontFile(const char* fontName, bool bold);
#endif
gboolean EmuLoop(gpointer data);
static AVOutX264 avout_x264;
@ -190,6 +202,9 @@ static void HudLagCounter(GSimpleAction *action, GVariant *parameter, gpointer u
static void HudRtc(GSimpleAction *action, GVariant *parameter, gpointer user_data);
static void HudMic(GSimpleAction *action, GVariant *parameter, gpointer user_data);
static void HudEditor(GSimpleAction *action, GVariant *parameter, gpointer user_data);
static void HudResetLayout(GSimpleAction *action, GVariant *parameter, gpointer user_data);
static void HudSaveLayout();
static void HudLoadLayout();
#endif
#ifdef DESMUME_GTK_FIRMWARE_BROKEN
static void SelectFirmwareFile(GSimpleAction *action, GVariant *parameter, gpointer user_data);
@ -247,6 +262,7 @@ static const GActionEntry app_entries[] = {
{ "hud_rtc", HudRtc, NULL, "false" },
{ "hud_mic", HudMic, NULL, "false" },
{ "hud_editor", HudEditor, NULL, "false" },
{ "hud_reset_layout", HudResetLayout},
#endif
// Config
@ -1449,7 +1465,7 @@ static void RedrawScreen() {
GPU->GetDisplayInfo().isCustomSizeRequested ? (u16*)(GPU->GetDisplayInfo().masterCustomBuffer) : GPU->GetDisplayInfo().masterNativeBuffer16,
(uint32_t *)video->GetSrcBufferPtr(), real_framebuffer_width * real_framebuffer_height * 2);
#ifdef HAVE_LIBAGG
aggDraw.hud->attach((u8*)video->GetSrcBufferPtr(), real_framebuffer_width, real_framebuffer_height * 2, 1024 * gpu_scale_factor);
aggDraw.hud->setDrawTargetDims((u8*)video->GetSrcBufferPtr(), real_framebuffer_width, real_framebuffer_height * 2, real_framebuffer_width * 4);
osd->update();
DrawHUD();
osd->clear();
@ -1471,33 +1487,33 @@ static gboolean rotoscaled_hudedit(gint x, gint y, gboolean start)
devX = x;
devY = y;
cairo_matrix_transform_point(&nds_screen.topscreen_matrix, &devX, &devY);
topX = devX;
topY = devY;
topX = devX * gpu_scale_factor;
topY = devY * gpu_scale_factor;
}
if (nds_screen.orientation != ORIENT_SINGLE || nds_screen.swap) {
devX = x;
devY = y;
cairo_matrix_transform_point(&nds_screen.touch_matrix, &devX, &devY);
botX = devX;
botY = devY;
botX = devX * gpu_scale_factor;
botY = devY * gpu_scale_factor;
}
if (topX >= 0 && topY >= 0 && topX < 256 && topY < 192) {
if (topX >= 0 && topY >= 0 && topX < real_framebuffer_width && topY < real_framebuffer_height) {
X = topX;
Y = topY + (nds_screen.swap ? 192 : 0);
Y = topY + (nds_screen.swap ? real_framebuffer_height : 0);
startScreen = 0;
} else if (botX >= 0 && botY >= 0 && botX < 256 && botY < 192) {
} else if (botX >= 0 && botY >= 0 && botX < real_framebuffer_width && botY < real_framebuffer_height) {
X = botX;
Y = botY + (nds_screen.swap ? 0 : 192);
Y = botY + (nds_screen.swap ? 0 : real_framebuffer_height);
startScreen = 1;
} else if (!start) {
if (startScreen == 0) {
X = CLAMP(topX, 0, 255);
Y = CLAMP(topY, 0, 191) + (nds_screen.swap ? 192 : 0);
X = CLAMP(topX, 0, real_framebuffer_width-1);
Y = CLAMP(topY, 0, real_framebuffer_height-1) + (nds_screen.swap ? real_framebuffer_height : 0);
} else {
X = CLAMP(botX, 0, 255);
Y = CLAMP(botY, 0, 191) + (nds_screen.swap ? 0 : 192);
X = CLAMP(botX, 0, real_framebuffer_width-1);
Y = CLAMP(botY, 0, real_framebuffer_height-1) + (nds_screen.swap ? 0 : real_framebuffer_height);
}
} else {
LOG("TopX=%d, TopY=%d, BotX=%d, BotY=%d\n", topX, topY, botX, botY);
@ -2098,6 +2114,7 @@ static void GraphicsSettingsDialog(GSimpleAction *action, GVariant *parameter, g
default:
break;
}
double old_scale_factor = gpu_scale_factor;
gpu_scale_factor = gtk_spin_button_get_value(wGPUScale);
if(gpu_scale_factor < GPU_SCALE_FACTOR_MIN)
gpu_scale_factor = GPU_SCALE_FACTOR_MIN;
@ -2109,6 +2126,21 @@ static void GraphicsSettingsDialog(GSimpleAction *action, GVariant *parameter, g
real_framebuffer_height = GPU_FRAMEBUFFER_NATIVE_HEIGHT * gpu_scale_factor;
GPU->SetCustomFramebufferSize(real_framebuffer_width, real_framebuffer_height);
video->SetSourceSize(real_framebuffer_width, real_framebuffer_height * 2);
#ifdef HAVE_LIBAGG
#ifdef AGG2D_USE_VECTORFONTS
if(vectorFontFile.size() > 0)
{
aggDraw.hud->setVectorFont(vectorFontFile, VECTOR_FONT_BASE_SIZE * gpu_scale_factor, true);
osd->useVectorFonts=(gpu_scale_factor >= 1.1);
}
else
osd->useVectorFonts=false;
#endif
Agg_setCustomSize(real_framebuffer_width, real_framebuffer_height*2);
osd->scale=gpu_scale_factor;
Hud.rescale(old_scale_factor, gpu_scale_factor);
HudSaveLayout();
#endif
CommonSettings.GFX3D_Renderer_TextureDeposterize = config.textureDeposterize = gtk_toggle_button_get_active(wPosterize);
CommonSettings.GFX3D_Renderer_TextureSmoothing = config.textureSmoothing = gtk_toggle_button_get_active(wSmoothing);
CommonSettings.GFX3D_Renderer_TextureScalingFactor = config.textureUpscale = scale;
@ -2773,6 +2805,8 @@ static void ToggleHudDisplay(hud_display_enum hudId, gboolean active)
break;
case HUD_DISPLAY_EDITOR:
HudEditorMode = active;
if(!active)
HudSaveLayout();
break;
default:
g_printerr("Unknown HUD toggle %u!", hudId);
@ -2799,6 +2833,60 @@ HudMacro(HudRtc, HUD_DISPLAY_RTC)
HudMacro(HudMic, HUD_DISPLAY_MIC)
HudMacro(HudEditor, HUD_DISPLAY_EDITOR)
static void HudResetLayout(GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
Hud.reset();
HudSaveLayout();
}
static void HudSaveCoordsToVector(HudCoordinates* pCoords, int* pDest)
{
pDest[0]=pCoords->x;
pDest[1]=pCoords->y;
pDest[2]=pCoords->xsize;
pDest[3]=pCoords->ysize;
}
static void HudLoadCoordsFromVector(HudCoordinates* pCoords, int* pSrc)
{
pCoords->x=pSrc[0];
pCoords->y=pSrc[1];
pCoords->xsize=pSrc[2];
pCoords->ysize=pSrc[3];
}
static void HudSaveLayout()
{
std::vector<int> vec(8*4); //8 HudCoordinates
HudSaveCoordsToVector(&Hud.SavestateSlots, vec.data());
HudSaveCoordsToVector(&Hud.FpsDisplay, vec.data()+4);
HudSaveCoordsToVector(&Hud.FrameCounter, vec.data()+8);
HudSaveCoordsToVector(&Hud.InputDisplay, vec.data()+12);
HudSaveCoordsToVector(&Hud.GraphicalInputDisplay, vec.data()+16);
HudSaveCoordsToVector(&Hud.LagFrameCounter, vec.data()+20);
HudSaveCoordsToVector(&Hud.Microphone, vec.data()+24);
HudSaveCoordsToVector(&Hud.RTCDisplay, vec.data()+28);
config.hud_layout=vec;
}
static void HudLoadLayout()
{
std::vector<int> vec=config.hud_layout;
if(vec.size()==8*4)
{
HudLoadCoordsFromVector(&Hud.SavestateSlots, vec.data());
HudLoadCoordsFromVector(&Hud.FpsDisplay, vec.data()+4);
HudLoadCoordsFromVector(&Hud.FrameCounter, vec.data()+8);
HudLoadCoordsFromVector(&Hud.InputDisplay, vec.data()+12);
HudLoadCoordsFromVector(&Hud.GraphicalInputDisplay, vec.data()+16);
HudLoadCoordsFromVector(&Hud.LagFrameCounter, vec.data()+20);
HudLoadCoordsFromVector(&Hud.Microphone, vec.data()+24);
HudLoadCoordsFromVector(&Hud.RTCDisplay, vec.data()+28);
}
else
Hud.reset();
}
static void desmume_gtk_menu_view_hud(GtkApplication *app)
{
const struct {
@ -2998,6 +3086,10 @@ common_gtk_main(GApplication *app, gpointer user_data)
/* Init the hud / osd stuff */
#ifdef HAVE_LIBAGG
SDL_DisplayMode cur_mode;
if(!SDL_GetCurrentDisplayMode(0, &cur_mode))
aggDraw.screenBytesPerPixel = SDL_BYTESPERPIXEL(cur_mode.format);
Desmume_InitOnce();
Hud.reset();
osd = new OSDCLASS(-1);
@ -3067,6 +3159,20 @@ common_gtk_main(GApplication *app, gpointer user_data)
g_printerr("Using %d threads for video filter.\n", CommonSettings.num_cores);
GPU->SetCustomFramebufferSize(real_framebuffer_width, real_framebuffer_height);
video = new VideoFilter(real_framebuffer_width, real_framebuffer_height * 2, VideoFilterTypeID_None, CommonSettings.num_cores);
#ifdef HAVE_LIBAGG
#ifdef AGG2D_USE_VECTORFONTS
if(vectorFontFile.size() > 0)
{
aggDraw.hud->setVectorFont(vectorFontFile, VECTOR_FONT_BASE_SIZE * gpu_scale_factor, true);
osd->useVectorFonts=(gpu_scale_factor >= 1.1);
}
else
osd->useVectorFonts=false;
#endif
Agg_setCustomSize(real_framebuffer_width, real_framebuffer_height*2);
osd->scale=gpu_scale_factor;
HudLoadLayout();
#endif
/* Fetch the main elements from the window */
GtkBuilder *builder = gtk_builder_new_from_resource("/org/desmume/DeSmuME/main.ui");
@ -3601,7 +3707,9 @@ common_gtk_main(GApplication *app, gpointer user_data)
static void Teardown() {
delete video;
#ifdef HAVE_LIBAGG
HudSaveLayout();
#endif
config.save();
avout_x264.end();
avout_flac.end();
@ -3644,10 +3752,46 @@ handle_open(GApplication *application,
common_gtk_main(application, user_data);
}
#ifdef AGG2D_USE_VECTORFONTS
static std::string FindFontFile(const char* fontName, bool bold)
{
std::string fontFile;
FcPattern* pat = FcNameParse((const FcChar8*)fontName);
if(bold)
FcPatternAddInteger(pat, FC_WEIGHT, FC_WEIGHT_BOLD);
FcConfigSubstitute(fontConfig, pat, FcMatchPattern);
FcDefaultSubstitute(pat);
// find the font
FcResult res;
FcPattern* font = FcFontMatch(fontConfig, pat, &res);
if (font)
{
FcChar8* file = NULL;
if (FcPatternGetString(font, FC_FILE, 0, &file) == FcResultMatch)
{
// save the file to another std::string
fontFile = (char*)file;
}
FcPatternDestroy(font);
}
FcPatternDestroy(pat);
return fontFile;
}
#endif
int main (int argc, char *argv[])
{
configured_features my_config;
#ifdef AGG2D_USE_VECTORFONTS
fontConfig = FcInitLoadConfigAndFonts();
vectorFontFile = FindFontFile("mono", true);
if(!vectorFontFile.size())
vectorFontFile = FindFontFile("sans", true);
#endif
// The global menu screws up the window size...
unsetenv("UBUNTU_MENUPROXY");

View File

@ -834,6 +834,10 @@
<attribute name='label' translatable='yes'>_Editor mode</attribute>
<attribute name='action'>app.hud_editor</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>Reset layout</attribute>
<attribute name='action'>app.hud_reset_layout</attribute>
</item>
</section>
</menu>
<menu id='hud_unsupported'>

View File

@ -20,6 +20,7 @@ dep_openal = dependency('openal', required: get_option('openal'))
dep_alsa = dependency('alsa', required: false)
dep_soundtouch = dependency('soundtouch', required: false)
dep_agg = dependency('libagg', required: false)
dep_fontconfig = dependency('fontconfig', required: false)
# XXX: something wrong with this one.
#dep_lua = dependency('lua-5.1', required: false)
@ -203,6 +204,11 @@ endif
if dep_agg.found()
dependencies += dep_agg
add_global_arguments('-DHAVE_LIBAGG', language: ['c', 'cpp'])
if dep_fontconfig.found()
dependencies += dep_fontconfig
add_global_arguments('-DAGG2D_USE_VECTORFONTS', language: ['c', 'cpp'])
add_global_link_arguments('-laggfontfreetype', language: ['c', 'cpp'])
endif
libdesmume_src += [
'../modules/osd/agg/aggdraw.cpp',
'../modules/osd/agg/agg_osd.cpp',