mirror of https://github.com/stella-emu/stella.git
added reading and displaying image labels
This commit is contained in:
parent
15ecfc1887
commit
b243e4867b
|
@ -38,7 +38,7 @@ PNGLibrary::PNGLibrary(OSystem& osystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
|
void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantList& comments)
|
||||||
{
|
{
|
||||||
png_structp png_ptr{nullptr};
|
png_structp png_ptr{nullptr};
|
||||||
png_infop info_ptr{nullptr};
|
png_infop info_ptr{nullptr};
|
||||||
|
@ -114,6 +114,9 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface)
|
||||||
// We're finished reading
|
// We're finished reading
|
||||||
png_read_end(png_ptr, info_ptr);
|
png_read_end(png_ptr, info_ptr);
|
||||||
|
|
||||||
|
// Read the comments we got
|
||||||
|
readComments(png_ptr, info_ptr, comments);
|
||||||
|
|
||||||
// Load image into the surface, setting the correct dimensions
|
// Load image into the surface, setting the correct dimensions
|
||||||
loadImagetoSurface(surface);
|
loadImagetoSurface(surface);
|
||||||
|
|
||||||
|
@ -335,6 +338,7 @@ void PNGLibrary::takeSnapshot(uInt32 number)
|
||||||
// Some text fields to add to the PNG snapshot
|
// Some text fields to add to the PNG snapshot
|
||||||
VariantList comments;
|
VariantList comments;
|
||||||
ostringstream version;
|
ostringstream version;
|
||||||
|
VarList::push_back(comments, "Title", "Snapshot");
|
||||||
version << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") ["
|
version << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") ["
|
||||||
<< BSPF::ARCH << "]";
|
<< BSPF::ARCH << "]";
|
||||||
VarList::push_back(comments, "Software", version.str());
|
VarList::push_back(comments, "Software", version.str());
|
||||||
|
@ -448,6 +452,23 @@ void PNGLibrary::writeComments(const png_structp png_ptr, png_infop info_ptr,
|
||||||
png_set_text(png_ptr, info_ptr, text_ptr.data(), numComments);
|
png_set_text(png_ptr, info_ptr, text_ptr.data(), numComments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void PNGLibrary::readComments(const png_structp png_ptr, png_infop info_ptr,
|
||||||
|
VariantList& comments)
|
||||||
|
{
|
||||||
|
png_textp text_ptr;
|
||||||
|
int numComments = 0;
|
||||||
|
|
||||||
|
// TODO: currently works only if comments are *before* the image data
|
||||||
|
png_get_text(png_ptr, info_ptr, &text_ptr, &numComments);
|
||||||
|
|
||||||
|
comments.clear();
|
||||||
|
for(int i = 0; i < numComments; ++i)
|
||||||
|
{
|
||||||
|
VarList::push_back(comments, text_ptr[i].key, text_ptr[i].text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void PNGLibrary::png_read_data(const png_structp ctx, png_bytep area, png_size_t size)
|
void PNGLibrary::png_read_data(const png_structp ctx, png_bytep area, png_size_t size)
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,7 +52,7 @@ class PNGLibrary
|
||||||
runtime_error is thrown containing a more detailed
|
runtime_error is thrown containing a more detailed
|
||||||
error message.
|
error message.
|
||||||
*/
|
*/
|
||||||
void loadImage(const string& filename, FBSurface& surface);
|
void loadImage(const string& filename, FBSurface& surface, VariantList& comments);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Save the current FrameBuffer image to a PNG file. Note that in most
|
Save the current FrameBuffer image to a PNG file. Note that in most
|
||||||
|
@ -183,6 +183,12 @@ class PNGLibrary
|
||||||
void writeComments(const png_structp png_ptr, png_infop info_ptr,
|
void writeComments(const png_structp png_ptr, png_infop info_ptr,
|
||||||
const VariantList& comments);
|
const VariantList& comments);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Read PNG tEXt chunks from the image.
|
||||||
|
*/
|
||||||
|
void readComments(const png_structp png_ptr, png_infop info_ptr,
|
||||||
|
VariantList& comments);
|
||||||
|
|
||||||
/** PNG library callback functions */
|
/** PNG library callback functions */
|
||||||
static void png_read_data(const png_structp ctx, png_bytep area, png_size_t size);
|
static void png_read_data(const png_structp ctx, png_bytep area, png_size_t size);
|
||||||
static void png_write_data(const png_structp ctx, png_bytep area, png_size_t size);
|
static void png_write_data(const png_structp ctx, png_bytep area, png_size_t size);
|
||||||
|
|
|
@ -305,8 +305,8 @@ void LauncherDialog::addRomWidgets(int ypos)
|
||||||
const int listHeight = _h - ypos - VBORDER - buttonHeight - VGAP * 3;
|
const int listHeight = _h - ypos - VBORDER - buttonHeight - VGAP * 3;
|
||||||
|
|
||||||
const float imgZoom = getRomInfoZoom(listHeight);
|
const float imgZoom = getRomInfoZoom(listHeight);
|
||||||
const int romWidth = imgZoom * TIAConstants::viewableWidth;
|
const int imageWidth = imgZoom * TIAConstants::viewableWidth;
|
||||||
const int listWidth = _w - (romWidth > 0 ? romWidth + fontWidth : 0) - HBORDER * 2;
|
const int listWidth = _w - (imageWidth > 0 ? imageWidth + fontWidth : 0) - HBORDER * 2;
|
||||||
|
|
||||||
// remember initial ROM directory for returning there via home button
|
// remember initial ROM directory for returning there via home button
|
||||||
instance().settings().setValue("startromdir", getRomDir());
|
instance().settings().setValue("startromdir", getRomDir());
|
||||||
|
@ -319,7 +319,7 @@ void LauncherDialog::addRomWidgets(int ypos)
|
||||||
wid.push_back(myList);
|
wid.push_back(myList);
|
||||||
|
|
||||||
// Add ROM info area (if enabled)
|
// Add ROM info area (if enabled)
|
||||||
if(romWidth > 0)
|
if(imageWidth > 0)
|
||||||
{
|
{
|
||||||
xpos += myList->getWidth() + fontWidth;
|
xpos += myList->getWidth() + fontWidth;
|
||||||
|
|
||||||
|
@ -327,16 +327,22 @@ void LauncherDialog::addRomWidgets(int ypos)
|
||||||
const Common::Size imgSize(TIAConstants::viewableWidth * imgZoom,
|
const Common::Size imgSize(TIAConstants::viewableWidth * imgZoom,
|
||||||
TIAConstants::viewableHeight * imgZoom);
|
TIAConstants::viewableHeight * imgZoom);
|
||||||
// Calculate font area, and in the process the font that can be used
|
// Calculate font area, and in the process the font that can be used
|
||||||
const Common::Size fontArea(romWidth - fontWidth * 2,
|
|
||||||
myList->getHeight() - imgSize.h - VGAP * 3);
|
// Infofont is unknown yet, but used in image label too. Assuming maximum font height.
|
||||||
|
int imageHeight = imgSize.h + RomImageWidget::labelHeight(_font);
|
||||||
|
|
||||||
|
const Common::Size fontArea(imageWidth - fontWidth * 2,
|
||||||
|
myList->getHeight() - imageHeight - VGAP * 4);
|
||||||
setRomInfoFont(fontArea);
|
setRomInfoFont(fontArea);
|
||||||
|
|
||||||
|
// Now we have the correct font height
|
||||||
|
imageHeight = imgSize.h + RomImageWidget::labelHeight(*myROMInfoFont);
|
||||||
myRomImageWidget = new RomImageWidget(this, *myROMInfoFont,
|
myRomImageWidget = new RomImageWidget(this, *myROMInfoFont,
|
||||||
xpos, ypos, romWidth, imgSize.h);
|
xpos, ypos, imageWidth, imageHeight);
|
||||||
|
|
||||||
int yofs = imgSize.h + _font.getFontHeight() / 2;
|
const int yofs = imageHeight + VGAP * 2;
|
||||||
myRomInfoWidget = new RomInfoWidget(this, *myROMInfoFont,
|
myRomInfoWidget = new RomInfoWidget(this, *myROMInfoFont,
|
||||||
xpos, ypos + yofs, romWidth, myList->getHeight() - yofs);
|
xpos, ypos + yofs, imageWidth, myList->getHeight() - yofs);
|
||||||
}
|
}
|
||||||
addToFocusList(wid);
|
addToFocusList(wid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font,
|
||||||
_flags = Widget::FLAG_ENABLED;
|
_flags = Widget::FLAG_ENABLED;
|
||||||
_bgcolor = kDlgColor;
|
_bgcolor = kDlgColor;
|
||||||
_bgcolorlo = kBGColorLo;
|
_bgcolorlo = kBGColorLo;
|
||||||
|
myImageHeight = _h - labelHeight(font);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -85,7 +86,7 @@ void RomImageWidget::parseProperties(const FSNode& node)
|
||||||
if(mySurface == nullptr)
|
if(mySurface == nullptr)
|
||||||
{
|
{
|
||||||
mySurface = instance().frameBuffer().allocateSurface(
|
mySurface = instance().frameBuffer().allocateSurface(
|
||||||
_w, _h, ScalingInterpolation::blur);
|
_w, myImageHeight, ScalingInterpolation::blur);
|
||||||
mySurface->applyAttributes();
|
mySurface->applyAttributes();
|
||||||
|
|
||||||
dialog().addRenderCallback([this]() {
|
dialog().addRenderCallback([this]() {
|
||||||
|
@ -105,24 +106,22 @@ void RomImageWidget::parseProperties(const FSNode& node)
|
||||||
// Get a valid filename representing a snapshot file for this rom and load the snapshot
|
// Get a valid filename representing a snapshot file for this rom and load the snapshot
|
||||||
const string& path = instance().snapshotLoadDir().getPath();
|
const string& path = instance().snapshotLoadDir().getPath();
|
||||||
|
|
||||||
|
myImageList.clear();
|
||||||
|
myImageIdx = 0;
|
||||||
// 1. Try to load snapshots by property name
|
// 1. Try to load snapshots by property name
|
||||||
if(getImageList(path + myProperties.get(PropType::Cart_Name)))
|
if(getImageList(path + myProperties.get(PropType::Cart_Name)))
|
||||||
mySurfaceIsValid = loadPng(myImageList[0].getPath());
|
mySurfaceIsValid = loadPng(myImageList[0].getPath());
|
||||||
//mySurfaceIsValid = loadPng(path + myProperties.get(PropType::Cart_Name) + ".png");
|
|
||||||
|
|
||||||
if(!mySurfaceIsValid)
|
// 2. Also try to load snapshot images by filename
|
||||||
{
|
|
||||||
// 2. If no snapshots with property name exists, try to load snapshot images by filename
|
|
||||||
if(getImageList(path + node.getNameWithExt("")))
|
if(getImageList(path + node.getNameWithExt("")))
|
||||||
mySurfaceIsValid = loadPng(myImageList[0].getPath());
|
mySurfaceIsValid = loadPng(myImageList[0].getPath());
|
||||||
//mySurfaceIsValid = loadPng(path + node.getNameWithExt("") + ".png");
|
|
||||||
|
|
||||||
if(!mySurfaceIsValid)
|
if(!mySurfaceIsValid)
|
||||||
{
|
{
|
||||||
// 3. If no ROM snapshots exist, try to load a default snapshot
|
// 3. If no ROM snapshots exist, try to load a default snapshot
|
||||||
mySurfaceIsValid = loadPng(path + "default_snapshot.png");
|
mySurfaceIsValid = loadPng(path + "default_snapshot.png");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
mySurfaceErrorMsg = "PNG image loading not supported";
|
mySurfaceErrorMsg = "PNG image loading not supported";
|
||||||
#endif
|
#endif
|
||||||
|
@ -144,7 +143,6 @@ bool RomImageWidget::getImageList(const string& filename)
|
||||||
|
|
||||||
FSNode node(instance().snapshotLoadDir().getPath());
|
FSNode node(instance().snapshotLoadDir().getPath());
|
||||||
|
|
||||||
myImageList.clear();
|
|
||||||
node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false);
|
node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false);
|
||||||
return myImageList.size() > 0;
|
return myImageList.size() > 0;
|
||||||
}
|
}
|
||||||
|
@ -154,14 +152,29 @@ bool RomImageWidget::loadPng(const string& filename)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
instance().png().loadImage(filename, *mySurface);
|
VariantList comments;
|
||||||
|
instance().png().loadImage(filename, *mySurface, comments);
|
||||||
|
|
||||||
// Scale surface to available image area
|
// Scale surface to available image area
|
||||||
const Common::Rect& src = mySurface->srcRect();
|
const Common::Rect& src = mySurface->srcRect();
|
||||||
const float scale = std::min(float(_w) / src.w(), float(_h) / src.h()) *
|
const float scale = std::min(float(_w) / src.w(), float(myImageHeight) / src.h()) *
|
||||||
instance().frameBuffer().hidpiScaleFactor();
|
instance().frameBuffer().hidpiScaleFactor();
|
||||||
mySurface->setDstSize(static_cast<uInt32>(src.w() * scale), static_cast<uInt32>(src.h() * scale));
|
mySurface->setDstSize(static_cast<uInt32>(src.w() * scale), static_cast<uInt32>(src.h() * scale));
|
||||||
|
|
||||||
|
// Retrieve label for loaded image
|
||||||
|
myLabel = "";
|
||||||
|
for(auto comment = comments.begin(); comment != comments.end(); ++comment)
|
||||||
|
{
|
||||||
|
if(comment->first == "Title")
|
||||||
|
{
|
||||||
|
myLabel = comment->second.toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(comment->first == "Software"
|
||||||
|
&& comment->second.toString().find("Stella") == 0)
|
||||||
|
myLabel = "Snapshot"; // default for Stella snapshots with missing "Title" comment
|
||||||
|
}
|
||||||
|
|
||||||
setDirty();
|
setDirty();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -177,7 +190,7 @@ bool RomImageWidget::loadPng(const string& filename)
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
|
void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
|
||||||
{
|
{
|
||||||
if(isEnabled() && x >= 0 && x < _w && y >= 0 && y < _h)
|
if(isEnabled() && x >= 0 && x < _w && y >= 0 && y < myImageHeight)
|
||||||
{
|
{
|
||||||
if(x < _w/2)
|
if(x < _w/2)
|
||||||
{
|
{
|
||||||
|
@ -186,7 +199,7 @@ void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount)
|
||||||
loadPng(myImageList[--myImageIdx].getPath());
|
loadPng(myImageList[--myImageIdx].getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(myImageIdx < myImageList.size())
|
else if(myImageIdx < myImageList.size() - 1)
|
||||||
{
|
{
|
||||||
loadPng(myImageList[++myImageIdx].getPath());
|
loadPng(myImageList[++myImageIdx].getPath());
|
||||||
}
|
}
|
||||||
|
@ -204,10 +217,10 @@ void RomImageWidget::handleMouseMoved(int x, int y)
|
||||||
void RomImageWidget::drawWidget(bool hilite)
|
void RomImageWidget::drawWidget(bool hilite)
|
||||||
{
|
{
|
||||||
FBSurface& s = dialog().surface();
|
FBSurface& s = dialog().surface();
|
||||||
const int yoff = _h + _font.getFontHeight() / 2;
|
const int yoff = myImageHeight;
|
||||||
|
|
||||||
s.fillRect(_x+2, _y+2, _w-4, _h-4, _bgcolor);
|
s.fillRect(_x+1, _y+1, _w-2, _h-1, _bgcolor);
|
||||||
s.frameRect(_x, _y, _w, _h, kColor);
|
s.frameRect(_x, _y, _w, myImageHeight, kColor);
|
||||||
|
|
||||||
if(!myHaveProperties)
|
if(!myHaveProperties)
|
||||||
{
|
{
|
||||||
|
@ -220,7 +233,7 @@ void RomImageWidget::drawWidget(bool hilite)
|
||||||
const Common::Rect& dst = mySurface->dstRect();
|
const Common::Rect& dst = mySurface->dstRect();
|
||||||
const uInt32 scale = instance().frameBuffer().hidpiScaleFactor();
|
const uInt32 scale = instance().frameBuffer().hidpiScaleFactor();
|
||||||
const uInt32 x = _x * scale + ((_w * scale - dst.w()) >> 1);
|
const uInt32 x = _x * scale + ((_w * scale - dst.w()) >> 1);
|
||||||
const uInt32 y = _y * scale + ((_h * scale - dst.h()) >> 1);
|
const uInt32 y = _y * scale + ((myImageHeight * scale - dst.h()) >> 1);
|
||||||
|
|
||||||
// Make sure when positioning the snapshot surface that we take
|
// Make sure when positioning the snapshot surface that we take
|
||||||
// the dialog surface position into account
|
// the dialog surface position into account
|
||||||
|
@ -233,6 +246,15 @@ void RomImageWidget::drawWidget(bool hilite)
|
||||||
const uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1);
|
const uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1);
|
||||||
s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor);
|
s.drawString(_font, mySurfaceErrorMsg, x, y, _w - 10, _textcolor);
|
||||||
}
|
}
|
||||||
|
ostringstream buf;
|
||||||
|
buf << myImageIdx + 1 << "/" << myImageList.size();
|
||||||
|
const int yText = _y + myImageHeight + _font.getFontHeight() / 8;
|
||||||
|
const int wText = _font.getStringWidth(buf.str());
|
||||||
|
|
||||||
|
if(myLabel.length())
|
||||||
|
s.drawString(_font, myLabel, _x, yText, _w - wText - _font.getMaxCharWidth() * 2, _textcolor);
|
||||||
|
if(myImageList.size())
|
||||||
|
s.drawString(_font, buf.str(), _x + _w - wText, yText, wText, _textcolor);
|
||||||
|
|
||||||
#ifdef PNG_SUPPORT
|
#ifdef PNG_SUPPORT
|
||||||
if(isHighlighted())
|
if(isHighlighted())
|
||||||
|
|
|
@ -31,6 +31,11 @@ class RomImageWidget : public Widget, public CommandSender
|
||||||
int x, int y, int w, int h);
|
int x, int y, int w, int h);
|
||||||
~RomImageWidget() override = default;
|
~RomImageWidget() override = default;
|
||||||
|
|
||||||
|
static int labelHeight(const GUI::Font& font)
|
||||||
|
{
|
||||||
|
return font.getFontHeight() * 9 / 8;
|
||||||
|
}
|
||||||
|
|
||||||
void setProperties(const FSNode& node, const string& md5);
|
void setProperties(const FSNode& node, const string& md5);
|
||||||
void clearProperties();
|
void clearProperties();
|
||||||
void reloadProperties(const FSNode& node);
|
void reloadProperties(const FSNode& node);
|
||||||
|
@ -66,6 +71,8 @@ class RomImageWidget : public Widget, public CommandSender
|
||||||
string mySurfaceErrorMsg;
|
string mySurfaceErrorMsg;
|
||||||
|
|
||||||
#ifdef PNG_SUPPORT
|
#ifdef PNG_SUPPORT
|
||||||
|
int myImageHeight{0};
|
||||||
|
|
||||||
// Contains the list of image names for the current ROM
|
// Contains the list of image names for the current ROM
|
||||||
FSList myImageList;
|
FSList myImageList;
|
||||||
|
|
||||||
|
@ -74,6 +81,9 @@ class RomImageWidget : public Widget, public CommandSender
|
||||||
|
|
||||||
// Current x-position of the mouse
|
// Current x-position of the mouse
|
||||||
int myMouseX{0};
|
int myMouseX{0};
|
||||||
|
|
||||||
|
// Label for the loaded image
|
||||||
|
string myLabel;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
Loading…
Reference in New Issue