diff --git a/src/common/PNGLibrary.cxx b/src/common/PNGLibrary.cxx index db7b17ffc..6612000de 100644 --- a/src/common/PNGLibrary.cxx +++ b/src/common/PNGLibrary.cxx @@ -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_infop info_ptr{nullptr}; @@ -114,6 +114,9 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface) // We're finished reading 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 loadImagetoSurface(surface); @@ -335,6 +338,7 @@ void PNGLibrary::takeSnapshot(uInt32 number) // Some text fields to add to the PNG snapshot VariantList comments; ostringstream version; + VarList::push_back(comments, "Title", "Snapshot"); version << "Stella " << STELLA_VERSION << " (Build " << STELLA_BUILD << ") [" << BSPF::ARCH << "]"; 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); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +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) { diff --git a/src/common/PNGLibrary.hxx b/src/common/PNGLibrary.hxx index 6770e7671..9d4d7f8a4 100644 --- a/src/common/PNGLibrary.hxx +++ b/src/common/PNGLibrary.hxx @@ -52,7 +52,7 @@ class PNGLibrary runtime_error is thrown containing a more detailed 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 @@ -183,6 +183,12 @@ class PNGLibrary void writeComments(const png_structp png_ptr, png_infop info_ptr, 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 */ 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); diff --git a/src/gui/LauncherDialog.cxx b/src/gui/LauncherDialog.cxx index 43e31b5a4..1f2233319 100644 --- a/src/gui/LauncherDialog.cxx +++ b/src/gui/LauncherDialog.cxx @@ -305,8 +305,8 @@ void LauncherDialog::addRomWidgets(int ypos) const int listHeight = _h - ypos - VBORDER - buttonHeight - VGAP * 3; const float imgZoom = getRomInfoZoom(listHeight); - const int romWidth = imgZoom * TIAConstants::viewableWidth; - const int listWidth = _w - (romWidth > 0 ? romWidth + fontWidth : 0) - HBORDER * 2; + const int imageWidth = imgZoom * TIAConstants::viewableWidth; + const int listWidth = _w - (imageWidth > 0 ? imageWidth + fontWidth : 0) - HBORDER * 2; // remember initial ROM directory for returning there via home button instance().settings().setValue("startromdir", getRomDir()); @@ -319,7 +319,7 @@ void LauncherDialog::addRomWidgets(int ypos) wid.push_back(myList); // Add ROM info area (if enabled) - if(romWidth > 0) + if(imageWidth > 0) { xpos += myList->getWidth() + fontWidth; @@ -327,16 +327,22 @@ void LauncherDialog::addRomWidgets(int ypos) const Common::Size imgSize(TIAConstants::viewableWidth * imgZoom, TIAConstants::viewableHeight * imgZoom); // 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); + // Now we have the correct font height + imageHeight = imgSize.h + RomImageWidget::labelHeight(*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, - xpos, ypos + yofs, romWidth, myList->getHeight() - yofs); + xpos, ypos + yofs, imageWidth, myList->getHeight() - yofs); } addToFocusList(wid); } diff --git a/src/gui/RomImageWidget.cxx b/src/gui/RomImageWidget.cxx index 1b383969b..a9dce62bc 100644 --- a/src/gui/RomImageWidget.cxx +++ b/src/gui/RomImageWidget.cxx @@ -36,6 +36,7 @@ RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font, _flags = Widget::FLAG_ENABLED; _bgcolor = kDlgColor; _bgcolorlo = kBGColorLo; + myImageHeight = _h - labelHeight(font); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -85,7 +86,7 @@ void RomImageWidget::parseProperties(const FSNode& node) if(mySurface == nullptr) { mySurface = instance().frameBuffer().allocateSurface( - _w, _h, ScalingInterpolation::blur); + _w, myImageHeight, ScalingInterpolation::blur); mySurface->applyAttributes(); 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 const string& path = instance().snapshotLoadDir().getPath(); + myImageList.clear(); + myImageIdx = 0; // 1. Try to load snapshots by property name if(getImageList(path + myProperties.get(PropType::Cart_Name))) 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) { - // 2. If no snapshots with property name exists, try to load snapshot images by filename - if(getImageList(path + node.getNameWithExt(""))) - 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"); - } + // 3. If no ROM snapshots exist, try to load a default snapshot + mySurfaceIsValid = loadPng(path + "default_snapshot.png"); } + #else mySurfaceErrorMsg = "PNG image loading not supported"; #endif @@ -144,7 +143,6 @@ bool RomImageWidget::getImageList(const string& filename) FSNode node(instance().snapshotLoadDir().getPath()); - myImageList.clear(); node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false); return myImageList.size() > 0; } @@ -154,13 +152,28 @@ bool RomImageWidget::loadPng(const string& filename) { try { - instance().png().loadImage(filename, *mySurface); + VariantList comments; + instance().png().loadImage(filename, *mySurface, comments); // Scale surface to available image area 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(); mySurface->setDstSize(static_cast(src.w() * scale), static_cast(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(); return true; @@ -177,7 +190,7 @@ bool RomImageWidget::loadPng(const string& filename) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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) { @@ -186,7 +199,7 @@ void RomImageWidget::handleMouseUp(int x, int y, MouseButton b, int clickCount) loadPng(myImageList[--myImageIdx].getPath()); } } - else if(myImageIdx < myImageList.size()) + else if(myImageIdx < myImageList.size() - 1) { loadPng(myImageList[++myImageIdx].getPath()); } @@ -204,10 +217,10 @@ void RomImageWidget::handleMouseMoved(int x, int y) void RomImageWidget::drawWidget(bool hilite) { 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.frameRect(_x, _y, _w, _h, kColor); + s.fillRect(_x+1, _y+1, _w-2, _h-1, _bgcolor); + s.frameRect(_x, _y, _w, myImageHeight, kColor); if(!myHaveProperties) { @@ -220,7 +233,7 @@ void RomImageWidget::drawWidget(bool hilite) const Common::Rect& dst = mySurface->dstRect(); const uInt32 scale = instance().frameBuffer().hidpiScaleFactor(); 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 // the dialog surface position into account @@ -233,6 +246,15 @@ void RomImageWidget::drawWidget(bool hilite) const uInt32 y = _y + ((yoff - _font.getLineHeight()) >> 1); 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 if(isHighlighted()) diff --git a/src/gui/RomImageWidget.hxx b/src/gui/RomImageWidget.hxx index 0af65822e..aca0d7004 100644 --- a/src/gui/RomImageWidget.hxx +++ b/src/gui/RomImageWidget.hxx @@ -31,6 +31,11 @@ class RomImageWidget : public Widget, public CommandSender int x, int y, int w, int h); ~RomImageWidget() override = default; + static int labelHeight(const GUI::Font& font) + { + return font.getFontHeight() * 9 / 8; + } + void setProperties(const FSNode& node, const string& md5); void clearProperties(); void reloadProperties(const FSNode& node); @@ -66,6 +71,8 @@ class RomImageWidget : public Widget, public CommandSender string mySurfaceErrorMsg; #ifdef PNG_SUPPORT + int myImageHeight{0}; + // Contains the list of image names for the current ROM FSList myImageList; @@ -74,6 +81,9 @@ class RomImageWidget : public Widget, public CommandSender // Current x-position of the mouse int myMouseX{0}; + + // Label for the loaded image + string myLabel; #endif private: