added reading and displaying image labels

This commit is contained in:
Thomas Jentzsch 2022-08-12 11:40:21 +02:00
parent 15ecfc1887
commit b243e4867b
5 changed files with 96 additions and 31 deletions

View File

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

View File

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

View File

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

View File

@ -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");
// 2. Also try to load snapshot images by filename
if(getImageList(path + node.getNameWithExt("")))
mySurfaceIsValid = loadPng(myImageList[0].getPath());
if(!mySurfaceIsValid) if(!mySurfaceIsValid)
{ {
// 2. If no snapshots with property name exists, try to load snapshot images by filename // 3. If no ROM snapshots exist, try to load a default snapshot
if(getImageList(path + node.getNameWithExt(""))) mySurfaceIsValid = loadPng(path + "default_snapshot.png");
mySurfaceIsValid = loadPng(myImageList[0].getPath());
//mySurfaceIsValid = loadPng(path + node.getNameWithExt("") + ".png");
if(!mySurfaceIsValid)
{
// 3. If no ROM snapshots exist, try to load a default snapshot
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,13 +152,28 @@ 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())

View File

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