mirror of https://github.com/stella-emu/stella.git
Added infrastructure to attach a surface to a dialog, so that it can be rendered
on top of the dialog surface. This is useful when the surfaces are using different resolutions, and we don't want to draw the exact overlaying surface pixels directly into the the dialog surface. For now, this is most useful for rendering snapshots in the ROM launcher, and eventually it will allow arbitrarily-sized images to be scaled (in hardware) to the picture area of the launcher. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2978 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
parent
5a89990cc1
commit
8da55d8eac
|
@ -26,6 +26,7 @@ FBSurfaceSDL2::FBSurfaceSDL2(FrameBufferSDL2& buffer,
|
|||
mySurface(NULL),
|
||||
myTexture(NULL),
|
||||
mySurfaceIsDirty(true),
|
||||
myIsVisible(true),
|
||||
myTexAccess(SDL_TEXTUREACCESS_STREAMING),
|
||||
myInterpolate(false),
|
||||
myBlendEnabled(false),
|
||||
|
@ -149,6 +150,12 @@ void FBSurfaceSDL2::setDstSize(uInt32 w, uInt32 h)
|
|||
myDstR.w = w; myDstR.h = h;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FBSurfaceSDL2::setVisible(bool visible)
|
||||
{
|
||||
myIsVisible = visible;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const
|
||||
{
|
||||
|
@ -157,13 +164,14 @@ void FBSurfaceSDL2::translateCoords(Int32& x, Int32& y) const
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void FBSurfaceSDL2::render()
|
||||
bool FBSurfaceSDL2::render()
|
||||
{
|
||||
if(mySurfaceIsDirty)
|
||||
if(mySurfaceIsDirty && myIsVisible)
|
||||
{
|
||||
//cerr << "src: x=" << mySrcR.x << ", y=" << mySrcR.y << ", w=" << mySrcR.w << ", h=" << mySrcR.h << endl;
|
||||
//cerr << "dst: x=" << myDstR.x << ", y=" << myDstR.y << ", w=" << myDstR.w << ", h=" << myDstR.h << endl;
|
||||
|
||||
//cerr << "render()\n";
|
||||
if(myTexAccess == SDL_TEXTUREACCESS_STREAMING)
|
||||
SDL_UpdateTexture(myTexture, &mySrcR, mySurface->pixels, mySurface->pitch);
|
||||
SDL_RenderCopy(myFB.myRenderer, myTexture, &mySrcR, &myDstR);
|
||||
|
@ -171,8 +179,9 @@ void FBSurfaceSDL2::render()
|
|||
mySurfaceIsDirty = false;
|
||||
|
||||
// Let postFrameUpdate() know that a change has been made
|
||||
myFB.myDirtyFlag = true;
|
||||
return myFB.myDirtyFlag = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -42,8 +42,7 @@ class FBSurfaceSDL2 : public FBSurface
|
|||
void fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color);
|
||||
void drawSurface(const FBSurface* surface);
|
||||
// With hardware surfaces, it's faster to just update the entire surface
|
||||
void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { mySurfaceIsDirty = true; }
|
||||
void addDirtyRect() { mySurfaceIsDirty = true; }
|
||||
void setDirty() { mySurfaceIsDirty = true; }
|
||||
|
||||
uInt32 width() const;
|
||||
uInt32 height() const;
|
||||
|
@ -54,9 +53,10 @@ class FBSurfaceSDL2 : public FBSurface
|
|||
void setSrcSize(uInt32 w, uInt32 h);
|
||||
void setDstPos(uInt32 x, uInt32 y);
|
||||
void setDstSize(uInt32 w, uInt32 h);
|
||||
void setVisible(bool visible);
|
||||
|
||||
void translateCoords(Int32& x, Int32& y) const;
|
||||
void render();
|
||||
bool render();
|
||||
void invalidate();
|
||||
void free();
|
||||
void reload();
|
||||
|
@ -72,6 +72,7 @@ class FBSurfaceSDL2 : public FBSurface
|
|||
SDL_Rect mySrcR, myDstR;
|
||||
|
||||
bool mySurfaceIsDirty;
|
||||
bool myIsVisible;
|
||||
|
||||
SDL_TextureAccess myTexAccess; // Is pixel data constant or can it change?
|
||||
bool myInterpolate; // Scaling is smoothed or blocky
|
||||
|
|
|
@ -219,16 +219,10 @@ class FBSurface
|
|||
virtual void drawSurface(const FBSurface* surface);
|
||||
|
||||
/**
|
||||
This method should be called to add a dirty rectangle
|
||||
(ie, an area of the screen that has changed)
|
||||
|
||||
@param x The x coordinate
|
||||
@param y The y coordinate
|
||||
@param w The width of the area
|
||||
@param h The height of the area
|
||||
This method should be called to indicate that the surface has been
|
||||
modified, and should be redrawn at the next interval.
|
||||
*/
|
||||
virtual void addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h) { }
|
||||
virtual void addDirtyRect() { }
|
||||
virtual void setDirty() { }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Note: The following methods are FBSurface-specific, and must be
|
||||
|
@ -262,6 +256,12 @@ class FBSurface
|
|||
virtual void setDstPos(uInt32 x, uInt32 y) = 0;
|
||||
virtual void setDstSize(uInt32 w, uInt32 h) = 0;
|
||||
|
||||
/**
|
||||
This method should be called to enable/disable showing the surface
|
||||
(ie, if hidden it will not be drawn under any circumstances.
|
||||
*/
|
||||
virtual void setVisible(bool visible) = 0;
|
||||
|
||||
/**
|
||||
This method should be called to translate the given coordinates
|
||||
to the (destination) surface coordinates.
|
||||
|
@ -273,8 +273,9 @@ class FBSurface
|
|||
|
||||
/**
|
||||
This method should be called to draw the surface to the screen.
|
||||
It will return true if rendering actually occurred.
|
||||
*/
|
||||
virtual void render() = 0;
|
||||
virtual bool render() = 0;
|
||||
|
||||
/**
|
||||
This method should be called to reset the surface to empty
|
||||
|
|
|
@ -302,7 +302,7 @@ void FrameBuffer::update()
|
|||
msg, 1, 1, myStatsMsg.w, myStatsMsg.color, kTextAlignLeft);
|
||||
myStatsMsg.surface->drawString(infoFont(),
|
||||
info.BankSwitch, 1, 15, myStatsMsg.w, myStatsMsg.color, kTextAlignLeft);
|
||||
myStatsMsg.surface->addDirtyRect(); // force a full draw
|
||||
myStatsMsg.surface->setDirty();
|
||||
myStatsMsg.surface->setDstPos(myImageRect.x() + 1, myImageRect.y() + 1);
|
||||
myStatsMsg.surface->render();
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ inline void FrameBuffer::drawMessage()
|
|||
}
|
||||
else
|
||||
{
|
||||
myMsg.surface->addDirtyRect();
|
||||
myMsg.surface->setDirty();
|
||||
myMsg.surface->render();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -226,7 +226,7 @@ uInt32 TIASurface::enableScanlines(int relative, int absolute)
|
|||
intensity = BSPF_min(100u, intensity);
|
||||
|
||||
mySLineSurface->applyAttributes();
|
||||
mySLineSurface->addDirtyRect();
|
||||
mySLineSurface->setDirty();
|
||||
|
||||
return intensity;
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ void TIASurface::enableScanlineInterpolation(bool enable)
|
|||
{
|
||||
mySLineSurface->myAttributes.smoothing = enable;
|
||||
mySLineSurface->applyAttributes();
|
||||
mySLineSurface->addDirtyRect();
|
||||
mySLineSurface->setDirty();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -245,8 +245,8 @@ void TIASurface::enablePhosphor(bool enable, int blend)
|
|||
myUsePhosphor = enable;
|
||||
myPhosphorBlend = blend;
|
||||
myFilterType = FilterType(enable ? myFilterType | 0x01 : myFilterType & 0x10);
|
||||
myTiaSurface->addDirtyRect();
|
||||
mySLineSurface->addDirtyRect();
|
||||
myTiaSurface->setDirty();
|
||||
mySLineSurface->setDirty();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -278,8 +278,8 @@ void TIASurface::enableNTSC(bool enable)
|
|||
myOSystem.settings().getInt("tv.scanlines");
|
||||
mySLineSurface->applyAttributes();
|
||||
|
||||
myTiaSurface->addDirtyRect();
|
||||
mySLineSurface->addDirtyRect();
|
||||
myTiaSurface->setDirty();
|
||||
mySLineSurface->setDirty();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -375,13 +375,13 @@ void TIASurface::render()
|
|||
}
|
||||
|
||||
// Draw TIA image
|
||||
myTiaSurface->addDirtyRect();
|
||||
myTiaSurface->setDirty();
|
||||
myTiaSurface->render();
|
||||
|
||||
// Draw overlaying scanlines
|
||||
if(myScanlinesEnabled)
|
||||
{
|
||||
mySLineSurface->addDirtyRect();
|
||||
mySLineSurface->setDirty();
|
||||
mySLineSurface->render();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -590,7 +590,7 @@ void ContextMenu::drawDialog()
|
|||
s.drawBitmap(down_arrow, ((_w-_x)>>1)-4, (_rowHeight>>1)+y-4, _scrollDnColor, 8);
|
||||
}
|
||||
|
||||
s.addDirtyRect(_x, _y, _w, _h);
|
||||
s.setDirty();
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -69,12 +69,6 @@ void Dialog::open(bool refresh)
|
|||
// Make sure we have a valid surface to draw into
|
||||
// Technically, this shouldn't be needed until drawDialog(), but some
|
||||
// dialogs cause drawing to occur within loadConfig()
|
||||
// Base surfaces are typically large, and will probably cause slow
|
||||
// performance if we update the whole area each frame
|
||||
// Instead, dirty rectangle updates should be performed
|
||||
// However, this policy is left entirely to the framebuffer
|
||||
// We suggest the hint here, but specific framebuffers are free to
|
||||
// ignore it
|
||||
if(_surface == NULL)
|
||||
{
|
||||
uInt32 surfaceID = instance().frameBuffer().allocateSurface(_w, _h);
|
||||
|
@ -274,6 +268,12 @@ void Dialog::redrawFocus()
|
|||
_focusedWidget = Widget::setFocusForChain(this, getFocusList(), _focusedWidget, 0);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Dialog::addSurface(FBSurface* surface)
|
||||
{
|
||||
mySurfaceStack.push(surface);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Dialog::draw()
|
||||
{
|
||||
|
@ -290,7 +290,6 @@ void Dialog::drawDialog()
|
|||
if(_dirty)
|
||||
{
|
||||
// cerr << "Dialog::drawDialog(): w = " << _w << ", h = " << _h << " @ " << &s << endl << endl;
|
||||
|
||||
s.fillRect(_x, _y, _w, _h, kDlgColor);
|
||||
s.box(_x, _y, _w, _h, kColor, kShadowColor);
|
||||
|
||||
|
@ -309,13 +308,22 @@ void Dialog::drawDialog()
|
|||
// Draw outlines for focused widgets
|
||||
redrawFocus();
|
||||
|
||||
// Tell the surface this area is dirty
|
||||
s.addDirtyRect(_x, _y, _w, _h);
|
||||
// Tell the surface(s) this area is dirty
|
||||
s.setDirty();
|
||||
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
// Commit surface changes to screen
|
||||
s.render();
|
||||
// Commit surface changes to screen; also render any extra surfaces
|
||||
// Extra surfaces must be rendered afterwards, so they are drawn on top
|
||||
if(s.render())
|
||||
{
|
||||
for(int i = 0; i < mySurfaceStack.size(); ++i)
|
||||
{
|
||||
mySurfaceStack[i]->setDirty();
|
||||
mySurfaceStack[i]->render();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
|
|
@ -29,6 +29,7 @@ class DialogContainer;
|
|||
class TabWidget;
|
||||
|
||||
#include "Command.hxx"
|
||||
#include "Stack.hxx"
|
||||
#include "Widget.hxx"
|
||||
#include "GuiObject.hxx"
|
||||
#include "StellaKeys.hxx"
|
||||
|
@ -72,8 +73,16 @@ class Dialog : public GuiObject
|
|||
void addCancelWidget(Widget* w) { _cancelWidget = w; }
|
||||
void setFocus(Widget* w);
|
||||
|
||||
/** Returns the base surface associated with this dialog. */
|
||||
FBSurface& surface() const { return *_surface; }
|
||||
|
||||
/** Adds a surface to this dialog, which is rendered on top of the
|
||||
base surface whenever the base surface is re-rendered. Since
|
||||
the surface render() call will always occur in such a case, the
|
||||
surface should call setVisible() to enable/disable its output.
|
||||
*/
|
||||
void addSurface(FBSurface* surface);
|
||||
|
||||
protected:
|
||||
virtual void draw();
|
||||
void releaseFocus();
|
||||
|
@ -115,6 +124,8 @@ class Dialog : public GuiObject
|
|||
bool _visible;
|
||||
bool _processCancel;
|
||||
|
||||
Common::FixedStack<FBSurface*> mySurfaceStack;
|
||||
|
||||
private:
|
||||
struct Focus {
|
||||
Widget* widget;
|
||||
|
|
|
@ -29,7 +29,6 @@ RomInfoWidget::RomInfoWidget(GuiObject* boss, const GUI::Font& font,
|
|||
int x, int y, int w, int h)
|
||||
: Widget(boss, font, x, y, w, h),
|
||||
mySurface(NULL),
|
||||
mySurfaceID(-1),
|
||||
myZoomLevel(w > 400 ? 2 : 1),
|
||||
mySurfaceIsValid(false),
|
||||
myHaveProperties(false)
|
||||
|
@ -75,6 +74,8 @@ void RomInfoWidget::setProperties(const Properties& props)
|
|||
void RomInfoWidget::clearProperties()
|
||||
{
|
||||
myHaveProperties = mySurfaceIsValid = false;
|
||||
if(mySurface)
|
||||
mySurface->setVisible(mySurfaceIsValid);
|
||||
|
||||
// Decide whether the information should be shown immediately
|
||||
if(instance().eventHandler().state() == EventHandler::S_LAUNCHER)
|
||||
|
@ -89,12 +90,12 @@ void RomInfoWidget::parseProperties()
|
|||
// Check if a surface has ever been created; if so, we use it
|
||||
// The surface will always be the maximum size, but sometimes we'll
|
||||
// only draw certain parts of it
|
||||
mySurface = instance().frameBuffer().surface(mySurfaceID);
|
||||
if(mySurface == NULL)
|
||||
{
|
||||
mySurfaceID = instance().frameBuffer().allocateSurface(
|
||||
uInt32 ID = instance().frameBuffer().allocateSurface(
|
||||
320*myZoomLevel, 256*myZoomLevel);
|
||||
mySurface = instance().frameBuffer().surface(mySurfaceID);
|
||||
mySurface = instance().frameBuffer().surface(ID);
|
||||
dialog().addSurface(mySurface);
|
||||
}
|
||||
|
||||
// Initialize to empty properties entry
|
||||
|
@ -116,6 +117,8 @@ void RomInfoWidget::parseProperties()
|
|||
mySurfaceIsValid = false;
|
||||
mySurfaceErrorMsg = msg;
|
||||
}
|
||||
if(mySurface)
|
||||
mySurface->setVisible(mySurfaceIsValid);
|
||||
|
||||
// Now add some info for the message box below the image
|
||||
myRomInfo.push_back("Name: " + myProperties.get(Cartridge_Name));
|
||||
|
@ -145,8 +148,11 @@ void RomInfoWidget::drawWidget(bool hilite)
|
|||
const GUI::Rect& src = mySurface->srcRect();
|
||||
uInt32 x = _x + ((_w - src.width()) >> 1);
|
||||
uInt32 y = _y + ((yoff - src.height()) >> 1);
|
||||
mySurface->setDstPos(x, y);
|
||||
s.drawSurface(mySurface);
|
||||
|
||||
// Make sure when positioning the snapshot surface that we take
|
||||
// the dialog surface position into account
|
||||
const GUI::Rect& s_dst = s.dstRect();
|
||||
mySurface->setDstPos(x + s_dst.x(), y + s_dst.y());
|
||||
}
|
||||
else if(mySurfaceErrorMsg != "")
|
||||
{
|
||||
|
|
|
@ -47,9 +47,8 @@ class RomInfoWidget : public Widget
|
|||
void parseProperties();
|
||||
|
||||
private:
|
||||
// Surface id and pointer holding the scaled PNG image
|
||||
// Surface pointer holding the PNG image
|
||||
FBSurface* mySurface;
|
||||
int mySurfaceID;
|
||||
|
||||
// How much to zoom the PNG image
|
||||
int myZoomLevel;
|
||||
|
|
|
@ -75,7 +75,7 @@ void Widget::draw()
|
|||
FBSurface& s = _boss->dialog().surface();
|
||||
|
||||
bool hasBorder = _flags & WIDGET_BORDER;
|
||||
int oldX = _x, oldY = _y, oldW = _w, oldH = _h;
|
||||
int oldX = _x, oldY = _y;
|
||||
|
||||
// Account for our relative position in the dialog
|
||||
_x = getAbsX();
|
||||
|
@ -93,7 +93,8 @@ void Widget::draw()
|
|||
}
|
||||
|
||||
// Draw border
|
||||
if(hasBorder) {
|
||||
if(hasBorder)
|
||||
{
|
||||
s.box(_x, _y, _w, _h, kColor, kShadowColor);
|
||||
_x += 4;
|
||||
_y += 4;
|
||||
|
@ -105,7 +106,8 @@ void Widget::draw()
|
|||
drawWidget((_flags & WIDGET_HILITED) ? true : false);
|
||||
|
||||
// Restore x/y
|
||||
if (hasBorder) {
|
||||
if (hasBorder)
|
||||
{
|
||||
_x -= 4;
|
||||
_y -= 4;
|
||||
_w += 8;
|
||||
|
@ -124,7 +126,7 @@ void Widget::draw()
|
|||
}
|
||||
|
||||
// Tell the framebuffer this area is dirty
|
||||
s.addDirtyRect(getAbsX(), getAbsY(), oldW, oldH);
|
||||
s.setDirty();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -226,7 +228,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
|
|||
s.frameRect(x, y, w, h, kDlgColor);
|
||||
|
||||
tmp->setDirty(); tmp->draw();
|
||||
s.addDirtyRect(x, y, w, h);
|
||||
s.setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,7 +271,7 @@ Widget* Widget::setFocusForChain(GuiObject* boss, WidgetArray& arr,
|
|||
s.frameRect(x, y, w, h, kWidFrameColor, kDashLine);
|
||||
|
||||
tmp->setDirty(); tmp->draw();
|
||||
s.addDirtyRect(x, y, w, h);
|
||||
s.setDirty();
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue