diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj
index d5d9f992b2..7a5234d01a 100644
--- a/rpcs3/emucore.vcxproj
+++ b/rpcs3/emucore.vcxproj
@@ -760,6 +760,7 @@
+
diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters
index d08488e266..50e3e666a7 100644
--- a/rpcs3/emucore.vcxproj.filters
+++ b/rpcs3/emucore.vcxproj.filters
@@ -2716,6 +2716,9 @@
Emu\GPU\RSX\Program
+
+ Utilities
+
diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj
index 7c39b0a88c..acee714f24 100644
--- a/rpcs3/rpcs3.vcxproj
+++ b/rpcs3/rpcs3.vcxproj
@@ -807,6 +807,7 @@
+
@@ -1514,6 +1515,7 @@
.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp
"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWIN32_LEAN_AND_MEAN -DHAVE_VULKAN -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_LIB -DQT_MULTIMEDIAWIDGETS_LIB -DQT_SVG_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\SoundTouch\soundtouch\include" "-I.\..\3rdparty\cubeb\extra" "-I.\..\3rdparty\cubeb\cubeb\include" "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia" "-I$(QTDIR)\include\QtMultimediaWidgets" "-I$(QTDIR)\include\QtSvg"
+
$(QTDIR)\bin\moc.exe;%(FullPath)
diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters
index a2e4afd1a6..0bb0890b41 100644
--- a/rpcs3/rpcs3.vcxproj.filters
+++ b/rpcs3/rpcs3.vcxproj.filters
@@ -196,6 +196,9 @@
{9b51636c-b371-425b-86d3-be003774a1b7}
+
+ {2bb5cec5-5acb-40c0-a388-68db05dff305}
+
@@ -1176,6 +1179,9 @@
Gui\game list
+
+ Io\video
+
@@ -1385,6 +1391,9 @@
Gui\game list
+
+ Io\video
+
diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt
index ffe2a96d9f..66c4fa3144 100644
--- a/rpcs3/rpcs3qt/CMakeLists.txt
+++ b/rpcs3/rpcs3qt/CMakeLists.txt
@@ -73,6 +73,7 @@ add_library(rpcs3_ui STATIC
qt_camera_video_sink.cpp
qt_music_handler.cpp
qt_utils.cpp
+ qt_video_source.cpp
raw_mouse_settings_dialog.cpp
register_editor_dialog.cpp
recvmessage_dialog_frame.cpp
diff --git a/rpcs3/rpcs3qt/game_list_base.cpp b/rpcs3/rpcs3qt/game_list_base.cpp
index 631bf4c066..72b45d33bf 100644
--- a/rpcs3/rpcs3qt/game_list_base.cpp
+++ b/rpcs3/rpcs3qt/game_list_base.cpp
@@ -33,7 +33,7 @@ void game_list_base::repaint_icons(std::vector& game_data, const QCol
IconLoadFunction(game, device_pixel_ratio, cancel);
});
- item->call_icon_func();
+ item->image_change_callback();
}
}
}
diff --git a/rpcs3/rpcs3qt/game_list_grid.cpp b/rpcs3/rpcs3qt/game_list_grid.cpp
index 30b8cd0745..5d4b90203f 100644
--- a/rpcs3/rpcs3qt/game_list_grid.cpp
+++ b/rpcs3/rpcs3qt/game_list_grid.cpp
@@ -21,7 +21,7 @@ game_list_grid::game_list_grid()
connect(this, &game_list_grid::IconReady, this, [this](const movie_item_base* item)
{
- if (item) item->call_icon_func();
+ if (item) item->image_change_callback();
}, Qt::QueuedConnection); // The default 'AutoConnection' doesn't seem to work in this specific case...
connect(this, &flow_widget::ItemSelectionChanged, this, [this](int index)
@@ -81,7 +81,7 @@ void game_list_grid::populate(
item->setToolTip(tr("%0 [%1]").arg(title).arg(serial));
}
- item->set_icon_func([this, item, game](const QVideoFrame& frame)
+ item->set_image_change_callback([this, item, game](const QVideoFrame& frame)
{
if (!item || !game)
{
@@ -109,7 +109,7 @@ void game_list_grid::populate(
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
{
- item->set_movie_path(QString::fromStdString(game->info.movie_path));
+ item->set_video_path(game->info.movie_path);
}
if (selected_item_id == game->info.path + game->info.icon_path)
@@ -154,7 +154,7 @@ void game_list_grid::repaint_icons(std::vector& game_data, const QCol
{
// We don't have an icon. Set a placeholder to initialize the layout.
game->pxmap = placeholder;
- item->call_icon_func();
+ item->image_change_callback();
}
item->set_icon_load_func([this, game, device_pixel_ratio, cancel = item->icon_loading_aborted()](int)
diff --git a/rpcs3/rpcs3qt/game_list_table.cpp b/rpcs3/rpcs3qt/game_list_table.cpp
index 3aed99dcf9..f526bcea43 100644
--- a/rpcs3/rpcs3qt/game_list_table.cpp
+++ b/rpcs3/rpcs3qt/game_list_table.cpp
@@ -54,7 +54,7 @@ game_list_table::game_list_table(game_list_frame* frame, std::shared_ptrcall_icon_func();
+ if (item) item->image_change_callback();
});
}
@@ -242,7 +242,7 @@ void game_list_table::populate(
custom_table_widget_item* icon_item = new custom_table_widget_item;
game->item = icon_item;
- icon_item->set_icon_func([this, icon_item, game](const QVideoFrame& frame)
+ icon_item->set_image_change_callback([this, icon_item, game](const QVideoFrame& frame)
{
if (!icon_item || !game)
{
@@ -292,7 +292,7 @@ void game_list_table::populate(
if (play_hover_movies && (game->has_hover_gif || game->has_hover_pam))
{
- icon_item->set_movie_path(QString::fromStdString(game->info.movie_path));
+ icon_item->set_video_path(game->info.movie_path);
}
icon_item->setData(Qt::UserRole, index, true);
diff --git a/rpcs3/rpcs3qt/movie_item_base.cpp b/rpcs3/rpcs3qt/movie_item_base.cpp
index d413f4d0b3..fdf820ae5f 100644
--- a/rpcs3/rpcs3qt/movie_item_base.cpp
+++ b/rpcs3/rpcs3qt/movie_item_base.cpp
@@ -1,24 +1,14 @@
#include "stdafx.h"
#include "movie_item_base.h"
-#include
-
-movie_item_base::movie_item_base()
+movie_item_base::movie_item_base() : qt_video_source()
{
init_pointers();
}
movie_item_base::~movie_item_base()
{
- if (m_movie)
- {
- m_movie->stop();
- }
-
- if (m_media_player)
- {
- m_media_player->stop();
- }
+ stop_movie();
wait_for_icon_loading(true);
wait_for_size_on_disk_loading(true);
@@ -30,146 +20,6 @@ void movie_item_base::init_pointers()
m_size_on_disk_loading_aborted.reset(new atomic_t(false));
}
-void movie_item_base::set_active(bool active)
-{
- if (!std::exchange(m_active, active) && active)
- {
- init_movie();
-
- if (m_movie)
- {
- m_movie->jumpToFrame(1);
- m_movie->start();
- }
-
- if (m_media_player)
- {
- m_media_player->play();
- }
- }
-}
-
-void movie_item_base::init_movie()
-{
- if (m_movie || m_media_player)
- {
- // Already initialized
- return;
- }
-
- if (!m_icon_callback || m_movie_path.isEmpty() || !QFile::exists(m_movie_path))
- {
- m_movie_path.clear();
- return;
- }
-
- const QString lower = m_movie_path.toLower();
-
- if (lower.endsWith(".gif"))
- {
- m_movie.reset(new QMovie(m_movie_path));
- m_movie_path.clear();
-
- if (!m_movie->isValid())
- {
- m_movie.reset();
- return;
- }
-
- QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), [this](int)
- {
- m_icon_callback({});
- });
- return;
- }
-
- if (lower.endsWith(".pam"))
- {
- // We can't set PAM files as source of the video player, so we have to feed them as raw data.
- QFile file(m_movie_path);
- if (!file.open(QFile::OpenModeFlag::ReadOnly))
- {
- return;
- }
-
- // TODO: Decode the pam properly before pushing it to the player
- m_movie_data = file.readAll();
- if (m_movie_data.isEmpty())
- {
- return;
- }
-
- m_movie_buffer.reset(new QBuffer(&m_movie_data));
- m_movie_buffer->open(QIODevice::ReadOnly);
- }
-
- m_video_sink.reset(new QVideoSink());
- QObject::connect(m_video_sink.get(), &QVideoSink::videoFrameChanged, m_video_sink.get(), [this](const QVideoFrame& frame)
- {
- m_icon_callback(frame);
- });
-
- m_media_player.reset(new QMediaPlayer());
- m_media_player->setVideoSink(m_video_sink.get());
- m_media_player->setLoops(QMediaPlayer::Infinite);
-
- if (m_movie_buffer)
- {
- m_media_player->setSourceDevice(m_movie_buffer.get());
- }
- else
- {
- m_media_player->setSource(m_movie_path);
- }
-}
-
-void movie_item_base::stop_movie()
-{
- if (m_movie)
- {
- m_movie->stop();
- }
-
- m_video_sink.reset();
- m_media_player.reset();
- m_movie_buffer.reset();
- m_movie_data.clear();
-}
-
-QPixmap movie_item_base::get_movie_image(const QVideoFrame& frame) const
-{
- if (!m_active)
- {
- return {};
- }
-
- if (m_movie)
- {
- return m_movie->currentPixmap();
- }
-
- if (!frame.isValid())
- {
- return {};
- }
-
- // Get image. This usually also converts the image to ARGB32.
- return QPixmap::fromImage(frame.toImage());
-}
-
-void movie_item_base::call_icon_func() const
-{
- if (m_icon_callback)
- {
- m_icon_callback({});
- }
-}
-
-void movie_item_base::set_icon_func(const icon_callback_t& func)
-{
- m_icon_callback = func;
-}
-
void movie_item_base::call_icon_load_func(int index)
{
if (!m_icon_load_callback || m_icon_loading || m_icon_loading_aborted->load())
diff --git a/rpcs3/rpcs3qt/movie_item_base.h b/rpcs3/rpcs3qt/movie_item_base.h
index f13bce91b2..3784de4e1f 100644
--- a/rpcs3/rpcs3qt/movie_item_base.h
+++ b/rpcs3/rpcs3qt/movie_item_base.h
@@ -1,25 +1,16 @@
#pragma once
-#include "movie_item_base.h"
-#include "util/atomic.hpp"
-#include "Utilities/mutex.h"
+#include "qt_video_source.h"
-#include
#include
-#include
-#include
-#include
-#include
-#include
#include
#include
-using icon_callback_t = std::function;
using icon_load_callback_t = std::function;
using size_calc_callback_t = std::function;
-class movie_item_base
+class movie_item_base : public qt_video_source
{
public:
movie_item_base();
@@ -27,25 +18,6 @@ public:
void init_pointers();
- void set_active(bool active);
-
- [[nodiscard]] bool get_active() const
- {
- return m_active;
- }
-
- void set_movie_path(QString path)
- {
- m_movie_path = std::move(path);
- }
-
- void init_movie();
- void stop_movie();
- QPixmap get_movie_image(const QVideoFrame& frame) const;
-
- void call_icon_func() const;
- void set_icon_func(const icon_callback_t& func);
-
void call_icon_load_func(int index);
void set_icon_load_func(const icon_load_callback_t& func);
@@ -77,23 +49,13 @@ public:
shared_mutex pixmap_mutex;
-protected:
- QString m_movie_path;
- QByteArray m_movie_data{};
- std::unique_ptr m_movie_buffer;
- std::unique_ptr m_media_player;
- std::shared_ptr m_video_sink;
- std::shared_ptr m_movie;
-
private:
std::unique_ptr m_icon_load_thread;
std::unique_ptr m_size_calc_thread;
- bool m_active = false;
atomic_t m_size_on_disk_loading = false;
atomic_t m_icon_loading = false;
size_calc_callback_t m_size_calc_callback = nullptr;
icon_load_callback_t m_icon_load_callback = nullptr;
- icon_callback_t m_icon_callback = nullptr;
std::shared_ptr> m_icon_loading_aborted;
std::shared_ptr> m_size_on_disk_loading_aborted;
diff --git a/rpcs3/rpcs3qt/qt_video_source.cpp b/rpcs3/rpcs3qt/qt_video_source.cpp
new file mode 100644
index 0000000000..263c1f2121
--- /dev/null
+++ b/rpcs3/rpcs3qt/qt_video_source.cpp
@@ -0,0 +1,244 @@
+#include "stdafx.h"
+#include "Emu/System.h"
+#include "qt_video_source.h"
+
+#include
+
+qt_video_source::qt_video_source()
+ : video_source()
+{
+}
+
+qt_video_source::~qt_video_source()
+{
+ stop_movie();
+}
+
+void qt_video_source::set_video_path(const std::string& path)
+{
+ m_video_path = QString::fromStdString(path);
+}
+
+void qt_video_source::set_active(bool active)
+{
+ if (!m_active.exchange(active) && active)
+ {
+ start_movie();
+ }
+}
+
+void qt_video_source::image_change_callback() const
+{
+ if (m_image_change_callback)
+ {
+ m_image_change_callback({});
+ }
+}
+
+void qt_video_source::set_image_change_callback(const std::function& func)
+{
+ m_image_change_callback = func;
+}
+
+void qt_video_source::init_movie()
+{
+ if (m_movie || m_media_player)
+ {
+ // Already initialized
+ return;
+ }
+
+ if (!m_image_change_callback || m_video_path.isEmpty() || !QFile::exists(m_video_path))
+ {
+ m_video_path.clear();
+ return;
+ }
+
+ const QString lower = m_video_path.toLower();
+
+ if (lower.endsWith(".gif"))
+ {
+ m_movie.reset(new QMovie(m_video_path));
+ m_video_path.clear();
+
+ if (!m_movie->isValid())
+ {
+ m_movie.reset();
+ return;
+ }
+
+ QObject::connect(m_movie.get(), &QMovie::frameChanged, m_movie.get(), [this](int)
+ {
+ m_image_change_callback({});
+ m_has_new = true;
+ });
+ return;
+ }
+
+ if (lower.endsWith(".pam"))
+ {
+ // We can't set PAM files as source of the video player, so we have to feed them as raw data.
+ QFile file(m_video_path);
+ if (!file.open(QFile::OpenModeFlag::ReadOnly))
+ {
+ return;
+ }
+
+ // TODO: Decode the pam properly before pushing it to the player
+ m_video_data = file.readAll();
+ if (m_video_data.isEmpty())
+ {
+ return;
+ }
+
+ m_video_buffer.reset(new QBuffer(&m_video_data));
+ m_video_buffer->open(QIODevice::ReadOnly);
+ }
+
+ m_video_sink.reset(new QVideoSink());
+ QObject::connect(m_video_sink.get(), &QVideoSink::videoFrameChanged, m_video_sink.get(), [this](const QVideoFrame& frame)
+ {
+ m_image_change_callback(frame);
+ m_has_new = true;
+ });
+
+ m_media_player.reset(new QMediaPlayer());
+ m_media_player->setVideoSink(m_video_sink.get());
+ m_media_player->setLoops(QMediaPlayer::Infinite);
+
+ if (m_video_buffer)
+ {
+ m_media_player->setSourceDevice(m_video_buffer.get());
+ }
+ else
+ {
+ m_media_player->setSource(m_video_path);
+ }
+}
+
+void qt_video_source::start_movie()
+{
+ init_movie();
+
+ if (m_movie)
+ {
+ m_movie->jumpToFrame(1);
+ m_movie->start();
+ }
+
+ if (m_media_player)
+ {
+ m_media_player->play();
+ }
+
+ m_active = true;
+}
+
+void qt_video_source::stop_movie()
+{
+ m_active = false;
+
+ if (m_movie)
+ {
+ m_movie->stop();
+ }
+
+ m_video_sink.reset();
+ m_media_player.reset();
+ m_video_buffer.reset();
+ m_video_data.clear();
+}
+
+QPixmap qt_video_source::get_movie_image(const QVideoFrame& frame) const
+{
+ if (!m_active)
+ {
+ return {};
+ }
+
+ if (m_movie)
+ {
+ return m_movie->currentPixmap();
+ }
+
+ if (!frame.isValid())
+ {
+ return {};
+ }
+
+ // Get image. This usually also converts the image to ARGB32.
+ return QPixmap::fromImage(frame.toImage());
+}
+
+void qt_video_source::get_image(std::vector& data, int& w, int& h, int& ch, int& bpp)
+{
+ if (!m_has_new.exchange(false))
+ {
+ return;
+ }
+
+ std::lock_guard lock(m_image_mutex);
+
+ if (m_image.isNull())
+ {
+ w = h = ch = bpp = 0;
+ data.clear();
+ return;
+ }
+
+ w = m_image.width();
+ h = m_image.height();
+ ch = m_image.colorCount();
+ bpp = m_image.depth();
+
+ data.resize(m_image.height() * m_image.bytesPerLine());
+ std::memcpy(data.data(), m_image.constBits(), data.size());
+}
+
+qt_video_source_wrapper::~qt_video_source_wrapper()
+{
+ Emu.BlockingCallFromMainThread([this]()
+ {
+ m_qt_video_source.reset();
+ });
+}
+
+void qt_video_source_wrapper::set_video_path(const std::string& path)
+{
+ Emu.BlockingCallFromMainThread([this, &path]()
+ {
+ m_qt_video_source = std::make_unique();
+ m_qt_video_source->m_image_change_callback = [this](const QVideoFrame& frame)
+ {
+ std::lock_guard lock(m_qt_video_source->m_image_mutex);
+
+ if (m_qt_video_source->m_movie)
+ {
+ m_qt_video_source->m_image = m_qt_video_source->m_movie->currentImage();
+ }
+ else if (frame.isValid())
+ {
+ // Get image. This usually also converts the image to ARGB32.
+ m_qt_video_source->m_image = frame.toImage();
+ }
+ else
+ {
+ return;
+ }
+
+ if (m_qt_video_source->m_image.format() != QImage::Format_RGBA8888)
+ {
+ m_qt_video_source->m_image.convertTo(QImage::Format_RGBA8888);
+ }
+ };
+ m_qt_video_source->set_video_path(path);
+ m_qt_video_source->set_active(true);
+ });
+}
+
+void qt_video_source_wrapper::get_image(std::vector& data, int& w, int& h, int& ch, int& bpp)
+{
+ ensure(m_qt_video_source);
+
+ m_qt_video_source->get_image(data, w, h, ch, bpp);
+}
diff --git a/rpcs3/rpcs3qt/qt_video_source.h b/rpcs3/rpcs3qt/qt_video_source.h
new file mode 100644
index 0000000000..44cdebc034
--- /dev/null
+++ b/rpcs3/rpcs3qt/qt_video_source.h
@@ -0,0 +1,76 @@
+#pragma once
+
+#include "util/video_source.h"
+#include "util/atomic.hpp"
+#include "Utilities/mutex.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+class qt_video_source : public video_source
+{
+public:
+ qt_video_source();
+ virtual ~qt_video_source();
+
+ void set_video_path(const std::string& path) override;
+ const QString& video_path() const { return m_video_path; }
+
+ void get_image(std::vector& data, int& w, int& h, int& ch, int& bpp) override;
+ bool has_new() const override { return m_has_new; }
+
+ void set_active(bool active);
+ [[nodiscard]] bool get_active() const
+ {
+ return m_active;
+ }
+
+ void start_movie();
+ void stop_movie();
+
+ QPixmap get_movie_image(const QVideoFrame& frame) const;
+
+ void image_change_callback() const;
+ void set_image_change_callback(const std::function& func);
+
+protected:
+ void init_movie();
+
+ shared_mutex m_image_mutex;
+
+ atomic_t m_active = false;
+ atomic_t m_has_new = false;
+
+ QString m_video_path;
+ QByteArray m_video_data{};
+ QImage m_image{};
+ std::vector m_image_path;
+
+ std::unique_ptr m_video_buffer;
+ std::unique_ptr m_media_player;
+ std::shared_ptr m_video_sink;
+ std::shared_ptr m_movie;
+
+ std::function m_image_change_callback = nullptr;
+
+ friend class qt_video_source_wrapper;
+};
+
+// Wrapper for emulator usage
+class qt_video_source_wrapper : public video_source
+{
+public:
+ qt_video_source_wrapper() : video_source() {}
+ virtual ~qt_video_source_wrapper();
+
+ void set_video_path(const std::string& path) override;
+ void get_image(std::vector& data, int& w, int& h, int& ch, int& bpp) override;
+ bool has_new() const override { return m_qt_video_source && m_qt_video_source->has_new(); }
+
+private:
+ std::unique_ptr m_qt_video_source;
+};
diff --git a/rpcs3/rpcs3qt/save_manager_dialog.cpp b/rpcs3/rpcs3qt/save_manager_dialog.cpp
index 1485d12ec0..bdb085d7df 100644
--- a/rpcs3/rpcs3qt/save_manager_dialog.cpp
+++ b/rpcs3/rpcs3qt/save_manager_dialog.cpp
@@ -208,7 +208,7 @@ void save_manager_dialog::Init()
if (movie_item* item = static_cast(m_list->item(index, SaveColumns::Icon)))
{
item->setData(SaveUserRole::PixmapScaled, pixmap);
- item->call_icon_func();
+ item->image_change_callback();
}
});
connect(search_bar, &QLineEdit::textChanged, this, &save_manager_dialog::text_changed);
@@ -344,14 +344,14 @@ void save_manager_dialog::UpdateList()
if (const std::string movie_path = dir_path + localized_movie; fs::is_file(movie_path))
{
- icon_item->set_movie_path(QString::fromStdString(movie_path));
+ icon_item->set_video_path(movie_path);
}
else if (const std::string movie_path = dir_path + "ICON1.PAM"; fs::is_file(movie_path))
{
- icon_item->set_movie_path(QString::fromStdString(movie_path));
+ icon_item->set_video_path(movie_path);
}
- icon_item->set_icon_func([this, icon_item](const QVideoFrame& frame)
+ icon_item->set_image_change_callback([this, icon_item](const QVideoFrame& frame)
{
if (!icon_item)
{
diff --git a/rpcs3/util/video_source.h b/rpcs3/util/video_source.h
new file mode 100644
index 0000000000..b5737bccc0
--- /dev/null
+++ b/rpcs3/util/video_source.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "types.hpp"
+
+class video_source
+{
+public:
+ video_source() {};
+ virtual ~video_source() {};
+ virtual void set_video_path(const std::string& path) { static_cast(path); }
+ virtual bool has_new() const { return false; };
+ virtual void get_image(std::vector& data, int& w, int& h, int& ch, int& bpp)
+ {
+ static_cast(data);
+ static_cast(w);
+ static_cast(h);
+ static_cast(ch);
+ static_cast(bpp);
+ }
+};