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:
stephena 2014-08-24 21:47:51 +00:00
parent 5a89990cc1
commit 8da55d8eac
11 changed files with 90 additions and 53 deletions

View File

@ -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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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

View File

@ -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

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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();
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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;

View File

@ -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 != "")
{

View File

@ -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;

View File

@ -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;
}