made ROM info delay adaptive

some cleanup of the new JPG reading code
This commit is contained in:
Thomas Jentzsch 2022-08-17 09:17:04 +02:00
parent 6e4710750f
commit 7691b2606f
9 changed files with 153 additions and 126 deletions

View File

@ -33,7 +33,7 @@ JPGLibrary::JPGLibrary(OSystem& osystem)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void JPGLibrary::loadImage(const string& filename, FBSurface& surface, void JPGLibrary::loadImage(const string& filename, FBSurface& surface,
VariantList& comments) VariantList& metaData)
{ {
const auto loadImageERROR = [&](const char* s) { const auto loadImageERROR = [&](const char* s) {
if(s) if(s)
@ -63,8 +63,8 @@ void JPGLibrary::loadImage(const string& filename, FBSurface& surface,
myReadInfo.height = njGetHeight(); myReadInfo.height = njGetHeight();
myReadInfo.pitch = myReadInfo.width * 3; myReadInfo.pitch = myReadInfo.width * 3;
// Read the comments we got TODO // TODO: Read the meta data we got
//readComments(png_ptr, info_ptr, comments); //readMetaData(png_ptr, info_ptr, metaData);
// Load image into the surface, setting the correct dimensions // Load image into the surface, setting the correct dimensions
loadImagetoSurface(surface); loadImagetoSurface(surface);
@ -103,8 +103,8 @@ void JPGLibrary::loadImagetoSurface(FBSurface& surface)
} }
//// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//void JPGLibrary::readComments(const png_structp png_ptr, png_infop info_ptr, //void JPGLibrary::readMetaData(const png_structp png_ptr, png_infop info_ptr,
// VariantList& comments) // VariantList& metaData)
//{ //{
// png_textp text_ptr; // png_textp text_ptr;
// int numComments = 0; // int numComments = 0;
@ -112,10 +112,10 @@ void JPGLibrary::loadImagetoSurface(FBSurface& surface)
// // TODO: currently works only if comments are *before* the image data // // TODO: currently works only if comments are *before* the image data
// png_get_text(png_ptr, info_ptr, &text_ptr, &numComments); // png_get_text(png_ptr, info_ptr, &text_ptr, &numComments);
// //
// comments.clear(); // metaData.clear();
// for(int i = 0; i < numComments; ++i) // for(int i = 0; i < numComments; ++i)
// { // {
// VarList::push_back(comments, text_ptr[i].key, text_ptr[i].text); // VarList::push_back(metaData, text_ptr[i].key, text_ptr[i].text);
// } // }
//} //}

View File

@ -40,12 +40,13 @@ class JPGLibrary
@param filename The filename to load the JPG image @param filename The filename to load the JPG image
@param surface The FBSurface into which to place the JPG data @param surface The FBSurface into which to place the JPG data
@param metaData The meta data of the JPG image
@post On success, the FBSurface containing image data, otherwise a @post On success, the FBSurface containing image data, otherwise a
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, VariantList& comments); void loadImage(const string& filename, FBSurface& surface, VariantList& metaData);
private: private:
// Global OSystem object // Global OSystem object
@ -70,10 +71,10 @@ class JPGLibrary
void loadImagetoSurface(FBSurface& surface); void loadImagetoSurface(FBSurface& surface);
///** ///**
// Read EXIF metadata chunks from the image. // Read EXIF meta data chunks from the image.
//*/ //*/
//void readComments(const png_structp png_ptr, png_infop info_ptr, //void readmetaData(const png_structp png_ptr, png_infop info_ptr,
// VariantList& comments); // VariantList& metaData);
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -33,7 +33,7 @@ PNGLibrary::PNGLibrary(OSystem& osystem)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantList& comments) void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantList& metaData)
{ {
png_structp png_ptr{nullptr}; png_structp png_ptr{nullptr};
png_infop info_ptr{nullptr}; png_infop info_ptr{nullptr};
@ -109,8 +109,8 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantLi
// 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 // Read the meta data we got
readComments(png_ptr, info_ptr, comments); readMetaData(png_ptr, info_ptr, metaData);
// Load image into the surface, setting the correct dimensions // Load image into the surface, setting the correct dimensions
loadImagetoSurface(surface); loadImagetoSurface(surface);
@ -121,7 +121,7 @@ void PNGLibrary::loadImage(const string& filename, FBSurface& surface, VariantLi
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImage(const string& filename, const VariantList& comments) void PNGLibrary::saveImage(const string& filename, const VariantList& metaData)
{ {
std::ofstream out(filename, std::ios_base::binary); std::ofstream out(filename, std::ios_base::binary);
if(!out.is_open()) if(!out.is_open())
@ -147,12 +147,12 @@ void PNGLibrary::saveImage(const string& filename, const VariantList& comments)
rows[k] = buffer.data() + k*width*4; rows[k] = buffer.data() + k*width*4;
// And save the image // And save the image
saveImageToDisk(out, rows, width, height, comments); saveImageToDisk(out, rows, width, height, metaData);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImage(const string& filename, const FBSurface& surface, void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
const Common::Rect& rect, const VariantList& comments) const Common::Rect& rect, const VariantList& metaData)
{ {
std::ofstream out(filename, std::ios_base::binary); std::ofstream out(filename, std::ios_base::binary);
if(!out.is_open()) if(!out.is_open())
@ -176,12 +176,12 @@ void PNGLibrary::saveImage(const string& filename, const FBSurface& surface,
rows[k] = buffer.data() + k*width*4; rows[k] = buffer.data() + k*width*4;
// And save the image // And save the image
saveImageToDisk(out, rows, width, height, comments); saveImageToDisk(out, rows, width, height, metaData);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows, void PNGLibrary::saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows,
png_uint_32 width, png_uint_32 height, const VariantList& comments) png_uint_32 width, png_uint_32 height, const VariantList& metaData)
{ {
png_structp png_ptr = nullptr; png_structp png_ptr = nullptr;
png_infop info_ptr = nullptr; png_infop info_ptr = nullptr;
@ -212,8 +212,8 @@ void PNGLibrary::saveImageToDisk(std::ofstream& out, const vector<png_bytep>& ro
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT); PNG_FILTER_TYPE_DEFAULT);
// Write comments // Write meta data
writeComments(png_ptr, info_ptr, comments); writeMetaData(png_ptr, info_ptr, metaData);
// Write the file header information. REQUIRED // Write the file header information. REQUIRED
png_write_info(png_ptr, info_ptr); png_write_info(png_ptr, info_ptr);
@ -331,18 +331,18 @@ void PNGLibrary::takeSnapshot(uInt32 number)
filename = sspath + ".png"; filename = sspath + ".png";
// Some text fields to add to the PNG snapshot // Some text fields to add to the PNG snapshot
VariantList comments; VariantList metaData;
ostringstream version; ostringstream version;
VarList::push_back(comments, "Title", "Snapshot"); VarList::push_back(metaData, "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(metaData, "Software", version.str());
const string& name = (myOSystem.settings().getString("snapname") == "int") const string& name = (myOSystem.settings().getString("snapname") == "int")
? myOSystem.console().properties().get(PropType::Cart_Name) ? myOSystem.console().properties().get(PropType::Cart_Name)
: myOSystem.romFile().getName(); : myOSystem.romFile().getName();
VarList::push_back(comments, "ROM Name", name); VarList::push_back(metaData, "ROM Name", name);
VarList::push_back(comments, "ROM MD5", myOSystem.console().properties().get(PropType::Cart_MD5)); VarList::push_back(metaData, "ROM MD5", myOSystem.console().properties().get(PropType::Cart_MD5));
VarList::push_back(comments, "TV Effects", myOSystem.frameBuffer().tiaSurface().effectsInfo()); VarList::push_back(metaData, "TV Effects", myOSystem.frameBuffer().tiaSurface().effectsInfo());
// Now create a PNG snapshot // Now create a PNG snapshot
string message = "Snapshot saved"; string message = "Snapshot saved";
@ -352,7 +352,7 @@ void PNGLibrary::takeSnapshot(uInt32 number)
{ {
Common::Rect rect; Common::Rect rect;
const FBSurface& surface = myOSystem.frameBuffer().tiaSurface().baseSurface(rect); const FBSurface& surface = myOSystem.frameBuffer().tiaSurface().baseSurface(rect);
myOSystem.png().saveImage(filename, surface, rect, comments); myOSystem.png().saveImage(filename, surface, rect, metaData);
} }
catch(const runtime_error& e) catch(const runtime_error& e)
{ {
@ -367,7 +367,7 @@ void PNGLibrary::takeSnapshot(uInt32 number)
try try
{ {
myOSystem.png().saveImage(filename, comments); myOSystem.png().saveImage(filename, metaData);
} }
catch(const runtime_error& e) catch(const runtime_error& e)
{ {
@ -429,38 +429,37 @@ void PNGLibrary::loadImagetoSurface(FBSurface& surface)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::writeComments(const png_structp png_ptr, png_infop info_ptr, void PNGLibrary::writeMetaData(const png_structp png_ptr, png_infop info_ptr,
const VariantList& comments) const VariantList& metaData)
{ {
const uInt32 numComments = static_cast<uInt32>(comments.size()); const uInt32 numMetaData = static_cast<uInt32>(metaData.size());
if(numComments == 0) if(numMetaData == 0)
return; return;
vector<png_text> text_ptr(numComments); vector<png_text> text_ptr(numMetaData);
for(uInt32 i = 0; i < numComments; ++i) for(uInt32 i = 0; i < numMetaData; ++i)
{ {
text_ptr[i].key = const_cast<char*>(comments[i].first.c_str()); text_ptr[i].key = const_cast<char*>(metaData[i].first.c_str());
text_ptr[i].text = const_cast<char*>(comments[i].second.toCString()); text_ptr[i].text = const_cast<char*>(metaData[i].second.toCString());
text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
text_ptr[i].text_length = 0; text_ptr[i].text_length = 0;
} }
png_set_text(png_ptr, info_ptr, text_ptr.data(), numComments); png_set_text(png_ptr, info_ptr, text_ptr.data(), numMetaData);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void PNGLibrary::readComments(const png_structp png_ptr, png_infop info_ptr, void PNGLibrary::readMetaData(const png_structp png_ptr, png_infop info_ptr,
VariantList& comments) VariantList& metaData)
{ {
png_textp text_ptr; png_textp text_ptr;
int numComments = 0; int numMetaData = 0;
// TODO: currently works only if comments are *before* the image data png_get_text(png_ptr, info_ptr, &text_ptr, &numMetaData);
png_get_text(png_ptr, info_ptr, &text_ptr, &numComments);
comments.clear(); metaData.clear();
for(int i = 0; i < numComments; ++i) for(int i = 0; i < numMetaData; ++i)
{ {
VarList::push_back(comments, text_ptr[i].key, text_ptr[i].text); VarList::push_back(metaData, text_ptr[i].key, text_ptr[i].text);
} }
} }

View File

@ -43,12 +43,13 @@ class PNGLibrary
@param filename The filename to load the PNG image @param filename The filename to load the PNG image
@param surface The FBSurface into which to place the PNG data @param surface The FBSurface into which to place the PNG data
@param metaData The meta data of the PNG image
@post On success, the FBSurface containing image data, otherwise a @post On success, the FBSurface containing image data, otherwise a
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, VariantList& comments); void loadImage(const string& filename, FBSurface& surface, VariantList& metaData);
/** /**
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
@ -56,14 +57,14 @@ class PNGLibrary
*any* mode. *any* mode.
@param filename The filename to save the PNG image @param filename The filename to save the PNG image
@param comments The text comments to add to the PNG image @param metaData The meta data s to add to the PNG image
@post On success, the PNG file has been saved to 'filename', @post On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a otherwise a runtime_error is thrown containing a
more detailed error message. more detailed error message.
*/ */
void saveImage(const string& filename, void saveImage(const string& filename,
const VariantList& comments = VariantList{}); const VariantList& metaData = VariantList{});
/** /**
Save the given surface to a PNG file. Save the given surface to a PNG file.
@ -71,7 +72,7 @@ class PNGLibrary
@param filename The filename to save the PNG image @param filename The filename to save the PNG image
@param surface The surface data for the PNG image @param surface The surface data for the PNG image
@param rect The area of the surface to use @param rect The area of the surface to use
@param comments The text comments to add to the PNG image @param metaData The meta data to add to the PNG image
@post On success, the PNG file has been saved to 'filename', @post On success, the PNG file has been saved to 'filename',
otherwise a runtime_error is thrown containing a otherwise a runtime_error is thrown containing a
@ -79,7 +80,7 @@ class PNGLibrary
*/ */
void saveImage(const string& filename, const FBSurface& surface, void saveImage(const string& filename, const FBSurface& surface,
const Common::Rect& rect = Common::Rect{}, const Common::Rect& rect = Common::Rect{},
const VariantList& comments = VariantList{}); const VariantList& metaData = VariantList{});
/** /**
Called at regular intervals, and used to determine whether a Called at regular intervals, and used to determine whether a
@ -155,15 +156,15 @@ class PNGLibrary
/** The actual method which saves a PNG image. /** The actual method which saves a PNG image.
@param out The output stream for writing PNG data @param out The output stream for writing PNG data
@param rows Pointer into PNG RGB data for each row @param rows Pointer into PNG RGB data for each row
@param width The width of the PNG image @param width The width of the PNG image
@param height The height of the PNG image @param height The height of the PNG image
@param comments The text comments to add to the PNG image @param metaData The meta data to add to the PNG image
*/ */
void saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows, void saveImageToDisk(std::ofstream& out, const vector<png_bytep>& rows,
png_uint_32 width, png_uint_32 height, png_uint_32 width, png_uint_32 height,
const VariantList& comments); const VariantList& metaData);
/** /**
Load the PNG data from 'ReadInfo' into the FBSurface. The surface Load the PNG data from 'ReadInfo' into the FBSurface. The surface
@ -176,14 +177,14 @@ class PNGLibrary
/** /**
Write PNG tEXt chunks to the image. Write PNG tEXt chunks to the image.
*/ */
void writeComments(const png_structp png_ptr, png_infop info_ptr, void writeMetaData(const png_structp png_ptr, png_infop info_ptr,
const VariantList& comments); const VariantList& metaData);
/** /**
Read PNG tEXt chunks from the image. Read PNG tEXt chunks from the image.
*/ */
void readComments(const png_structp png_ptr, png_infop info_ptr, void readMetaData(const png_structp png_ptr, png_infop info_ptr,
VariantList& comments); VariantList& metaData);
/** 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);

View File

@ -676,14 +676,23 @@ void LauncherDialog::loadRomInfo()
return; return;
// Update ROM info UI item, delayed // Update ROM info UI item, delayed
myRomInfoTime = TimerManager::getTicks() / 1000 + 250; // TODO: define pending load delay myRomInfoTime = TimerManager::getTicks() / 1000 + myRomImageWidget->pendingLoadTime();
myPendingRomInfo = true; myPendingRomInfo = true;
const string& md5 = selectedRomMD5(); const string& md5 = selectedRomMD5();
if(md5 != EmptyString) if(!md5.empty())
{ {
myRomImageWidget->setProperties(currentNode(), md5, false); // The properties for the currently selected ROM
myRomInfoWidget->setProperties(currentNode(), md5, false); Properties properties;
// Make sure to load a per-ROM properties entry, if one exists
instance().propSet().loadPerROM(currentNode(), md5);
// And now get the properties for this ROM
instance().propSet().getMD5(md5, properties);
myRomImageWidget->setProperties(currentNode(), properties, false);
myRomInfoWidget->setProperties(currentNode(), properties, false);
} }
else else
{ {
@ -703,8 +712,16 @@ void LauncherDialog::loadPendingRomInfo()
const string& md5 = selectedRomMD5(); const string& md5 = selectedRomMD5();
if(md5 != EmptyString) if(md5 != EmptyString)
{ {
myRomImageWidget->setProperties(currentNode(), md5); // The properties for the currently selected ROM
myRomInfoWidget->setProperties(currentNode(), md5); Properties properties;
// Make sure to load a per-ROM properties entry, if one exists
instance().propSet().loadPerROM(currentNode(), md5);
// And now get the properties for this ROM
instance().propSet().getMD5(md5, properties);
myRomImageWidget->setProperties(currentNode(), properties);
myRomInfoWidget->setProperties(currentNode(), properties);
} }
} }

View File

@ -25,13 +25,13 @@
#include "PNGLibrary.hxx" #include "PNGLibrary.hxx"
#include "Props.hxx" #include "Props.hxx"
#include "PropsSet.hxx" #include "PropsSet.hxx"
#include "TimerManager.hxx"
#include "RomImageWidget.hxx" #include "RomImageWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font, RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font,
int x, int y, int w, int h) int x, int y, int w, int h)
: Widget(boss, font, x, y, w, h), : Widget(boss, font, x, y, w, h)
CommandSender(boss)
{ {
_flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE; _flags = Widget::FLAG_ENABLED | Widget::FLAG_TRACK_MOUSE;
_bgcolor = kDlgColor; _bgcolor = kDlgColor;
@ -40,15 +40,10 @@ RomImageWidget::RomImageWidget(GuiObject* boss, const GUI::Font& font,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomImageWidget::setProperties(const FSNode& node, const string& md5, bool full) void RomImageWidget::setProperties(const FSNode& node, const Properties properties, bool full)
{ {
myHaveProperties = true; myHaveProperties = true;
myProperties = properties;
// Make sure to load a per-ROM properties entry, if one exists
instance().propSet().loadPerROM(node, md5);
// And now get the properties for this ROM
instance().propSet().getMD5(md5, myProperties);
// Decide whether the information should be shown immediately // Decide whether the information should be shown immediately
if(instance().eventHandler().state() == EventHandlerState::LAUNCHER) if(instance().eventHandler().state() == EventHandlerState::LAUNCHER)
@ -80,6 +75,7 @@ void RomImageWidget::reloadProperties(const FSNode& node)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomImageWidget::parseProperties(const FSNode& node, bool full) void RomImageWidget::parseProperties(const FSNode& node, bool full)
{ {
uInt64 startTime = TimerManager::getTicks() / 1000;
if(myNavSurface == nullptr) if(myNavSurface == nullptr)
{ {
// Create navigation surface // Create navigation surface
@ -119,23 +115,24 @@ void RomImageWidget::parseProperties(const FSNode& node, bool full)
// 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();
// 1. Try to load first snapshot by property name // 1. Try to load first snapshot by property name
string fileName = path + myProperties.get(PropType::Cart_Name);// +".png"; // TODO: try jpg string fileName = path + myProperties.get(PropType::Cart_Name);
tryImageFormats(fileName);
tryImageTypes(fileName);
loadImage(fileName);
if(!mySurfaceIsValid) if(!mySurfaceIsValid)
{ {
// 2. If none exists, try to load first snapshot by ROM file name // 2. If none exists, try to load first snapshot by ROM file name
fileName = path + node.getNameWithExt("png"); // TODO: try jpg fileName = path + node.getName();
loadImage(fileName); tryImageFormats(fileName);
} }
if(mySurfaceIsValid) if(mySurfaceIsValid)
myImageList.emplace_back(fileName); myImageList.emplace_back(fileName);
else else
{
// 3. If no ROM snapshots exist, try to load a default snapshot // 3. If no ROM snapshots exist, try to load a default snapshot
loadImage(path + "default_snapshot.png"); // TODO: try jpg??? fileName = path + "default_snapshot";
tryImageFormats(fileName);
}
} }
else else
{ {
@ -152,11 +149,16 @@ void RomImageWidget::parseProperties(const FSNode& node, bool full)
} }
#else #else
mySurfaceIsValid = false; mySurfaceIsValid = false;
mySurfaceErrorMsg = "PNG image loading not supported"; mySurfaceErrorMsg = "Image loading not supported";
setDirty(); setDirty();
#endif #endif
if(mySurface) if(mySurface)
mySurface->setVisible(mySurfaceIsValid); mySurface->setVisible(mySurfaceIsValid);
// Update maximum load time
myMaxLoadTime = std::min(
static_cast<uInt64>(500ull / timeFactor),
std::max(myMaxLoadTime, TimerManager::getTicks() / 1000 - startTime));
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -178,7 +180,7 @@ bool RomImageWidget::getImageList(const string& propName, const string& romName)
const std::regex symbols{R"([-[\]{}()*+?.,\^$|#])"}; // \s const std::regex symbols{R"([-[\]{}()*+?.,\^$|#])"}; // \s
const string rgxPropName = std::regex_replace(propName, symbols, R"(\$&)"); const string rgxPropName = std::regex_replace(propName, symbols, R"(\$&)");
const string rgxRomName = std::regex_replace(romName, symbols, R"(\$&)"); const string rgxRomName = std::regex_replace(romName, symbols, R"(\$&)");
// Look for <name.png> or <name_#.png> (# is a number) // Look for <name.png|jpg> or <name_#.png|jpg> (# is a number)
const std::regex rgx("^(" + rgxPropName + "|" + rgxRomName + ")(_\\d+){0,1}\\.(png|jpg)$"); const std::regex rgx("^(" + rgxPropName + "|" + rgxRomName + ")(_\\d+){0,1}\\.(png|jpg)$");
FSNode::NameFilter filter = ([&](const FSNode& node) FSNode::NameFilter filter = ([&](const FSNode& node)
@ -191,7 +193,7 @@ bool RomImageWidget::getImageList(const string& propName, const string& romName)
FSNode node(instance().snapshotLoadDir().getPath()); FSNode node(instance().snapshotLoadDir().getPath());
node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false); node.getChildren(myImageList, FSNode::ListMode::FilesOnly, filter, false, false);
// Sort again, not considering extensions, else <filename.png> would be at // Sort again, not considering extensions, else <filename.png|jpg> would be at
// the end of the list // the end of the list
std::sort(myImageList.begin(), myImageList.end(), std::sort(myImageList.begin(), myImageList.end(),
[](const FSNode& node1, const FSNode& node2) [](const FSNode& node1, const FSNode& node2)
@ -208,7 +210,7 @@ bool RomImageWidget::getImageList(const string& propName, const string& romName)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RomImageWidget::tryImageTypes(string& fileName) bool RomImageWidget::tryImageFormats(string& fileName)
{ {
if(loadImage(fileName + ".png")) if(loadImage(fileName + ".png"))
{ {
@ -235,6 +237,15 @@ bool RomImageWidget::loadImage(const string& fileName)
else else
mySurfaceIsValid = loadJpg(fileName); mySurfaceIsValid = loadJpg(fileName);
if(mySurfaceIsValid)
{
// Scale surface to available image area
const Common::Rect& src = mySurface->srcRect();
const float scale = std::min(float(_w) / src.w(), float(myImageHeight) / src.h()) *
instance().frameBuffer().hidpiScaleFactor();
mySurface->setDstSize(static_cast<uInt32>(src.w() * scale), static_cast<uInt32>(src.h() * scale));
}
if(mySurface) if(mySurface)
mySurface->setVisible(mySurfaceIsValid); mySurface->setVisible(mySurfaceIsValid);
@ -247,27 +258,21 @@ bool RomImageWidget::loadPng(const string& fileName)
{ {
try try
{ {
VariantList comments; VariantList metaData;
instance().png().loadImage(fileName, *mySurface, comments); instance().png().loadImage(fileName, *mySurface, metaData);
// Scale surface to available image area
const Common::Rect& src = mySurface->srcRect();
const float scale = std::min(float(_w) / src.w(), float(myImageHeight) / src.h()) *
instance().frameBuffer().hidpiScaleFactor();
mySurface->setDstSize(static_cast<uInt32>(src.w() * scale), static_cast<uInt32>(src.h() * scale));
// Retrieve label for loaded image // Retrieve label for loaded image
myLabel.clear(); myLabel.clear();
for(auto comment = comments.begin(); comment != comments.end(); ++comment) for(auto data = metaData.begin(); data != metaData.end(); ++data)
{ {
if(comment->first == "Title") if(data->first == "Title")
{ {
myLabel = comment->second.toString(); myLabel = data->second.toString();
break; break;
} }
if(comment->first == "Software" if(data->first == "Software"
&& comment->second.toString().find("Stella") == 0) && data->second.toString().find("Stella") == 0)
myLabel = "Snapshot"; // default for Stella snapshots with missing "Title" comment myLabel = "Snapshot"; // default for Stella snapshots with missing "Title" meta data
} }
return true; return true;
} }
@ -283,17 +288,17 @@ bool RomImageWidget::loadJpg(const string& fileName)
{ {
try try
{ {
VariantList comments; VariantList metaData;
instance().jpg().loadImage(fileName, *mySurface, comments); instance().jpg().loadImage(fileName, *mySurface, metaData);
myLabel.clear(); myLabel.clear();
// TODO
return true; return true;
} }
catch(const runtime_error& e) catch(const runtime_error& e)
{ {
mySurfaceErrorMsg = e.what(); mySurfaceErrorMsg = e.what();
} }
//mySurfaceErrorMsg = "JPG image loading not supported";
return false; return false;
} }

View File

@ -23,7 +23,7 @@ class Properties;
#include "Widget.hxx" #include "Widget.hxx"
class RomImageWidget : public Widget, public CommandSender class RomImageWidget : public Widget
{ {
public: public:
RomImageWidget(GuiObject *boss, const GUI::Font& font, RomImageWidget(GuiObject *boss, const GUI::Font& font,
@ -35,11 +35,13 @@ class RomImageWidget : public Widget, public CommandSender
return font.getFontHeight() * 9 / 8; return font.getFontHeight() * 9 / 8;
} }
void setProperties(const FSNode& node, const string& md5, bool full = true); void setProperties(const FSNode& node, const Properties properties, bool full = true);
void clearProperties(); void clearProperties();
void reloadProperties(const FSNode& node); void reloadProperties(const FSNode& node);
bool changeImage(int direction = 1); bool changeImage(int direction = 1);
uInt64 pendingLoadTime() { return myMaxLoadTime * timeFactor; }
protected: protected:
void drawWidget(bool hilite) override; void drawWidget(bool hilite) override;
#ifdef IMAGE_SUPPORT #ifdef IMAGE_SUPPORT
@ -51,14 +53,18 @@ class RomImageWidget : public Widget, public CommandSender
void parseProperties(const FSNode& node, bool full = true); void parseProperties(const FSNode& node, bool full = true);
#ifdef IMAGE_SUPPORT #ifdef IMAGE_SUPPORT
bool getImageList(const string& propName, const string& romName); bool getImageList(const string& propName, const string& romName);
bool tryImageTypes(string& fileName); bool tryImageFormats(string& fileName);
bool loadImage(const string& fileName); bool loadImage(const string& fileName);
bool loadPng(const string& fileName); bool loadPng(const string& fileName);
bool loadJpg(const string& fileName); bool loadJpg(const string& fileName);
#endif #endif
private: private:
// Surface pointer holding the PNG image // Pending load time safety factor
static constexpr double timeFactor = 1.2;
private:
// Surface pointer holding the image
shared_ptr<FBSurface> mySurface; shared_ptr<FBSurface> mySurface;
// Surface pointer holding the navigation elements // Surface pointer holding the navigation elements
@ -91,6 +97,9 @@ class RomImageWidget : public Widget, public CommandSender
// Label for the loaded image // Label for the loaded image
string myLabel; string myLabel;
// Maximum load time, for adapting pending loads delay
uInt64 myMaxLoadTime{0};
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
RomImageWidget() = delete; RomImageWidget() = delete;

View File

@ -39,15 +39,10 @@ RomInfoWidget::RomInfoWidget(GuiObject* boss, const GUI::Font& font,
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void RomInfoWidget::setProperties(const FSNode& node, const string& md5, bool full) void RomInfoWidget::setProperties(const FSNode& node, const Properties properties, bool full)
{ {
myHaveProperties = true; myHaveProperties = true;
myProperties = properties;
// Make sure to load a per-ROM properties entry, if one exists
instance().propSet().loadPerROM(node, md5);
// And now get the properties for this ROM
instance().propSet().getMD5(md5, myProperties);
// Decide whether the information should be shown immediately // Decide whether the information should be shown immediately
if(instance().eventHandler().state() == EventHandlerState::LAUNCHER) if(instance().eventHandler().state() == EventHandlerState::LAUNCHER)

View File

@ -35,7 +35,7 @@ class RomInfoWidget : public Widget, public CommandSender
int x, int y, int w, int h); int x, int y, int w, int h);
~RomInfoWidget() override = default; ~RomInfoWidget() override = default;
void setProperties(const FSNode& node, const string& md5, bool full = true); void setProperties(const FSNode& node, const Properties properties, bool full = true);
void clearProperties(); void clearProperties();
void reloadProperties(const FSNode& node); void reloadProperties(const FSNode& node);