1
0
Fork 0

Merge branch 'master' into core-macros-1

This commit is contained in:
Daniel Lim Wee Soong 2018-06-11 22:30:21 +08:00 committed by GitHub
commit f0ea96f144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 717 additions and 480 deletions

View File

@ -0,0 +1,15 @@
# List of environment variables to be shared with Docker containers
CI
TRAVIS
CONTINUOUS_INTEGRATION
TRAVIS_BRANCH
TRAVIS_BUILD_ID
TRAVIS_BUILD_NUMBER
TRAVIS_COMMIT
TRAVIS_JOB_ID
TRAVIS_JOB_NUMBER
TRAVIS_REPO_SLUG
TRAVIS_TAG
# citra specific flags
ENABLE_COMPATIBILITY_REPORTING

View File

@ -1,4 +1,4 @@
#!/bin/bash -ex #!/bin/bash -ex
mkdir -p "$HOME/.ccache" mkdir -p "$HOME/.ccache"
docker pull ubuntu:18.04 docker pull ubuntu:18.04
docker run -e ENABLE_COMPATIBILITY_REPORTING -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux-frozen/docker.sh docker run --env-file .travis/common/travis-ci.env -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux-frozen/docker.sh

View File

@ -1,3 +1,3 @@
#!/bin/bash -ex #!/bin/bash -ex
mkdir -p "$HOME/.ccache" mkdir -p "$HOME/.ccache"
docker run -e ENABLE_COMPATIBILITY_REPORTING -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux/docker.sh docker run --env-file .travis/common/travis-ci.env -v $(pwd):/citra -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /citra/.travis/linux/docker.sh

View File

@ -9,7 +9,21 @@ echo -e "\e[1m\e[33mInstalling dependencies...\e[0m"
apk update apk update
apk add build-base cmake python3-dev qt5-qttools-dev qt5-qtmultimedia-dev apk add build-base cmake python3-dev qt5-qttools-dev qt5-qtmultimedia-dev
pip3 install transifex-client pip3 install --upgrade pip transifex-client
cat << 'EOF' > /usr/bin/tx
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from txclib.cmdline import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(main())
EOF
echo -e "\e[1m\e[33mBuild tools information:\e[0m" echo -e "\e[1m\e[33mBuild tools information:\e[0m"
cmake --version cmake --version

View File

@ -27,13 +27,14 @@ install:
- ps: | - ps: |
if ($env:BUILD_TYPE -eq 'mingw') { if ($env:BUILD_TYPE -eq 'mingw') {
$dependencies = "mingw64/mingw-w64-x86_64-qt5" $dependencies = "mingw64/mingw-w64-x86_64-qt5"
C:\msys64\usr\bin\bash -lc "pacman -Syy"
# redirect err to null to prevent warnings from becoming errors # redirect err to null to prevent warnings from becoming errors
# workaround to prevent pacman from failing due to cyclical dependencies # workaround to prevent pacman from failing due to cyclical dependencies
C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw64/mingw-w64-x86_64-freetype mingw64/mingw-w64-x86_64-fontconfig" 2> $null C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw64/mingw-w64-x86_64-freetype mingw64/mingw-w64-x86_64-fontconfig" 2> $null
C:\msys64\usr\bin\bash -lc "pacman --noconfirm -U http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-SDL2-2.0.5-2-any.pkg.tar.xz" 2> $null C:\msys64\usr\bin\bash -lc "pacman --noconfirm -U http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-SDL2-2.0.5-2-any.pkg.tar.xz" 2> $null
C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S $dependencies" 2> $null C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S $dependencies" 2> $null
# stick to cmake 3.9.6 since on lower versions it could happen that cmake generates a Makefile that links against gcc_eh # freeze the cmake to a stable version, on version < 3.9.6 cmake may generate a Makefile links against gcc_eh instead of gcc_s_eh
C:\msys64\usr\bin\bash -lc "pacman --noconfirm -U http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-cmake-3.9.6-1-any.pkg.tar.xz" 2> $null C:\msys64\usr\bin\bash -lc "pacman --noconfirm -U http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-cmake-3.11.1-2-any.pkg.tar.xz" 2> $null
} }
before_build: before_build:

View File

@ -81,5 +81,5 @@ endif()
# Cubeb # Cubeb
if(ENABLE_CUBEB) if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "") set(BUILD_TESTS OFF CACHE BOOL "")
add_subdirectory(cubeb) add_subdirectory(cubeb EXCLUDE_FROM_ALL)
endif() endif()

@ -1 +1 @@
Subproject commit 5be140bcea453a00f7f2fec09fb9e37849d65d98 Subproject commit f320e7d92a33ee80ae42deef79da78cfc30868af

2
externals/dynarmic vendored

@ -1 +1 @@
Subproject commit d1d4705364031512cb89333aebc00b8d75a2f732 Subproject commit 4b350a354a21339052c7fff88832c3f81f5624be

@ -1 +1 @@
Subproject commit 019d2089bbadf70d73ba85aa8ea51490b071262c Subproject commit 060181eaf273180d3a7e87349895bd0cb6ccbf4a

View File

@ -155,14 +155,20 @@ void Config::ReadValues() {
sdl2_config->Get("Camera", "camera_outer_right_name", "blank"); sdl2_config->Get("Camera", "camera_outer_right_name", "blank");
Settings::values.camera_config[OuterRightCamera] = Settings::values.camera_config[OuterRightCamera] =
sdl2_config->Get("Camera", "camera_outer_right_config", ""); sdl2_config->Get("Camera", "camera_outer_right_config", "");
Settings::values.camera_flip[OuterRightCamera] =
sdl2_config->GetInteger("Camera", "camera_outer_right_flip", 0);
Settings::values.camera_name[InnerCamera] = Settings::values.camera_name[InnerCamera] =
sdl2_config->Get("Camera", "camera_inner_name", "blank"); sdl2_config->Get("Camera", "camera_inner_name", "blank");
Settings::values.camera_config[InnerCamera] = Settings::values.camera_config[InnerCamera] =
sdl2_config->Get("Camera", "camera_inner_config", ""); sdl2_config->Get("Camera", "camera_inner_config", "");
Settings::values.camera_flip[InnerCamera] =
sdl2_config->GetInteger("Camera", "camera_inner_flip", 0);
Settings::values.camera_name[OuterLeftCamera] = Settings::values.camera_name[OuterLeftCamera] =
sdl2_config->Get("Camera", "camera_outer_left_name", "blank"); sdl2_config->Get("Camera", "camera_outer_left_name", "blank");
Settings::values.camera_config[OuterLeftCamera] = Settings::values.camera_config[OuterLeftCamera] =
sdl2_config->Get("Camera", "camera_outer_left_config", ""); sdl2_config->Get("Camera", "camera_outer_left_config", "");
Settings::values.camera_flip[OuterLeftCamera] =
sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0);
// Miscellaneous // Miscellaneous
Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info"); Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");

View File

@ -178,13 +178,19 @@ camera_outer_right_name =
# A config string for the right outer camera. Its meaning is defined by the camera engine # A config string for the right outer camera. Its meaning is defined by the camera engine
camera_outer_right_config = camera_outer_right_config =
# The image flip to apply
# 0: None (default), 1: Horizontal, 2: Vertical, 3: Reverse
camera_outer_right_flip =
# ... for the left outer camera # ... for the left outer camera
camera_outer_left_name = camera_outer_left_name =
camera_outer_left_config = camera_outer_left_config =
camera_outer_left_flip =
# ... for the inner camera # ... for the inner camera
camera_inner_name = camera_inner_name =
camera_inner_config = camera_inner_config =
camera_inner_flip =
[Miscellaneous] [Miscellaneous]
# A filter which removes logs below a certain logging level. # A filter which removes logs below a certain logging level.

View File

@ -13,8 +13,8 @@ add_executable(citra-qt
camera/camera_util.h camera/camera_util.h
camera/still_image_camera.cpp camera/still_image_camera.cpp
camera/still_image_camera.h camera/still_image_camera.h
camera/qt_camera_factory.cpp camera/qt_camera_base.cpp
camera/qt_camera_factory.h camera/qt_camera_base.h
camera/qt_multimedia_camera.cpp camera/qt_multimedia_camera.cpp
camera/qt_multimedia_camera.h camera/qt_multimedia_camera.h
citra-qt.rc citra-qt.rc

View File

@ -0,0 +1,58 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QMessageBox>
#include "citra_qt/camera/camera_util.h"
#include "citra_qt/camera/qt_camera_base.h"
namespace Camera {
QtCameraInterface::QtCameraInterface(const Service::CAM::Flip& flip) {
using namespace Service::CAM;
flip_horizontal = basic_flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse);
flip_vertical = basic_flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse);
}
void QtCameraInterface::SetFormat(Service::CAM::OutputFormat output_format) {
output_rgb = output_format == Service::CAM::OutputFormat::RGB565;
}
void QtCameraInterface::SetResolution(const Service::CAM::Resolution& resolution) {
width = resolution.width;
height = resolution.height;
}
void QtCameraInterface::SetFlip(Service::CAM::Flip flip) {
using namespace Service::CAM;
flip_horizontal = basic_flip_horizontal ^ (flip == Flip::Horizontal || flip == Flip::Reverse);
flip_vertical = basic_flip_vertical ^ (flip == Flip::Vertical || flip == Flip::Reverse);
}
void QtCameraInterface::SetEffect(Service::CAM::Effect effect) {
if (effect != Service::CAM::Effect::None) {
NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect));
}
}
std::vector<u16> QtCameraInterface::ReceiveFrame() {
return CameraUtil::ProcessImage(QtReceiveFrame(), width, height, output_rgb, flip_horizontal,
flip_vertical);
}
std::unique_ptr<CameraInterface> QtCameraFactory::CreatePreview(const std::string& config,
int width, int height,
const Service::CAM::Flip& flip) {
std::unique_ptr<CameraInterface> camera = Create(config, flip);
if (camera->IsPreviewAvailable()) {
return camera;
}
QMessageBox::critical(
nullptr, QObject::tr("Error"),
(config.empty() ? QObject::tr("Couldn't load the camera")
: QObject::tr("Couldn't load %1").arg(QString::fromStdString(config))));
return nullptr;
}
} // namespace Camera

View File

@ -0,0 +1,36 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "core/frontend/camera/factory.h"
namespace Camera {
// Base class for camera interfaces of citra_qt
class QtCameraInterface : public CameraInterface {
public:
QtCameraInterface(const Service::CAM::Flip& flip);
void SetResolution(const Service::CAM::Resolution&) override;
void SetFlip(Service::CAM::Flip) override;
void SetEffect(Service::CAM::Effect) override;
void SetFormat(Service::CAM::OutputFormat) override;
std::vector<u16> ReceiveFrame() override;
virtual QImage QtReceiveFrame() = 0;
private:
int width, height;
bool output_rgb;
bool flip_horizontal, flip_vertical;
bool basic_flip_horizontal, basic_flip_vertical;
};
// Base class for camera factories of citra_qt
class QtCameraFactory : public CameraFactory {
std::unique_ptr<CameraInterface> CreatePreview(const std::string& config, int width, int height,
const Service::CAM::Flip& flip) override;
};
} // namespace Camera

View File

@ -1,24 +0,0 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QMessageBox>
#include "citra_qt/camera/qt_camera_factory.h"
namespace Camera {
std::unique_ptr<CameraInterface> QtCameraFactory::CreatePreview(const std::string& config,
int width, int height) const {
std::unique_ptr<CameraInterface> camera = Create(config);
if (camera->IsPreviewAvailable()) {
return camera;
}
QMessageBox::critical(
nullptr, QObject::tr("Error"),
(config.empty() ? QObject::tr("Couldn't load the camera")
: QObject::tr("Couldn't load %1").arg(QString::fromStdString(config))));
return nullptr;
}
} // namespace Camera

View File

@ -1,18 +0,0 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "core/frontend/camera/factory.h"
namespace Camera {
// Base class for camera factories of citra_qt
class QtCameraFactory : public CameraFactory {
std::unique_ptr<CameraInterface> CreatePreview(const std::string& config, int width,
int height) const override;
};
} // namespace Camera

View File

@ -46,8 +46,9 @@ bool QtCameraSurface::present(const QVideoFrame& frame) {
return true; return true;
} }
QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name) QtMultimediaCamera::QtMultimediaCamera(const std::string& camera_name,
: handler(QtMultimediaCameraHandler::GetHandler()) { const Service::CAM::Flip& flip)
: QtCameraInterface(flip), handler(QtMultimediaCameraHandler::GetHandler(camera_name)) {
if (handler->thread() == QThread::currentThread()) { if (handler->thread() == QThread::currentThread()) {
handler->CreateCamera(camera_name); handler->CreateCamera(camera_name);
} else { } else {
@ -73,10 +74,6 @@ void QtMultimediaCamera::StopCapture() {
handler->StopCamera(); handler->StopCamera();
} }
void QtMultimediaCamera::SetFormat(Service::CAM::OutputFormat output_format) {
output_rgb = output_format == Service::CAM::OutputFormat::RGB565;
}
void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) { void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) {
const std::array<QCamera::FrameRateRange, 13> FrameRateList = { const std::array<QCamera::FrameRateRange, 13> FrameRateList = {
/* Rate_15 */ QCamera::FrameRateRange(15, 15), /* Rate_15 */ QCamera::FrameRateRange(15, 15),
@ -96,57 +93,49 @@ void QtMultimediaCamera::SetFrameRate(Service::CAM::FrameRate frame_rate) {
auto framerate = FrameRateList[static_cast<int>(frame_rate)]; auto framerate = FrameRateList[static_cast<int>(frame_rate)];
handler->settings.setMinimumFrameRate(framerate.minimumFrameRate); if (handler->camera->supportedViewfinderFrameRateRanges().contains(framerate)) {
handler->settings.setMinimumFrameRate(framerate.maximumFrameRate); handler->settings.setMinimumFrameRate(framerate.minimumFrameRate);
} handler->settings.setMaximumFrameRate(framerate.maximumFrameRate);
void QtMultimediaCamera::SetResolution(const Service::CAM::Resolution& resolution) {
width = resolution.width;
height = resolution.height;
}
void QtMultimediaCamera::SetFlip(Service::CAM::Flip flip) {
using namespace Service::CAM;
flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse);
flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse);
}
void QtMultimediaCamera::SetEffect(Service::CAM::Effect effect) {
if (effect != Service::CAM::Effect::None) {
NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect));
} }
} }
std::vector<u16> QtMultimediaCamera::ReceiveFrame() { QImage QtMultimediaCamera::QtReceiveFrame() {
QMutexLocker locker(&handler->camera_surface.mutex); QMutexLocker locker(&handler->camera_surface.mutex);
return CameraUtil::ProcessImage(handler->camera_surface.current_frame, width, height, return handler->camera_surface.current_frame;
output_rgb, flip_horizontal, flip_vertical);
} }
bool QtMultimediaCamera::IsPreviewAvailable() { bool QtMultimediaCamera::IsPreviewAvailable() {
return handler->CameraAvailable(); return handler->CameraAvailable();
} }
std::unique_ptr<CameraInterface> QtMultimediaCameraFactory::Create( std::unique_ptr<CameraInterface> QtMultimediaCameraFactory::Create(const std::string& config,
const std::string& config) const { const Service::CAM::Flip& flip) {
return std::make_unique<QtMultimediaCamera>(config); return std::make_unique<QtMultimediaCamera>(config, flip);
} }
std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> QtMultimediaCameraHandler::handlers; std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> QtMultimediaCameraHandler::handlers;
std::array<bool, 3> QtMultimediaCameraHandler::status; std::array<bool, 3> QtMultimediaCameraHandler::status;
std::unordered_map<std::string, std::shared_ptr<QtMultimediaCameraHandler>>
QtMultimediaCameraHandler::loaded;
void QtMultimediaCameraHandler::Init() { void QtMultimediaCameraHandler::Init() {
for (auto& handler : handlers) { for (auto& handler : handlers) {
handler = std::make_shared<QtMultimediaCameraHandler>(); handler = std::make_shared<QtMultimediaCameraHandler>();
} }
} }
std::shared_ptr<QtMultimediaCameraHandler> QtMultimediaCameraHandler::GetHandler() { std::shared_ptr<QtMultimediaCameraHandler> QtMultimediaCameraHandler::GetHandler(
const std::string& camera_name) {
if (loaded.count(camera_name)) {
return loaded.at(camera_name);
}
for (int i = 0; i < handlers.size(); i++) { for (int i = 0; i < handlers.size(); i++) {
if (!status[i]) { if (!status[i]) {
NGLOG_INFO(Service_CAM, "Successfully got handler {}", i); NGLOG_INFO(Service_CAM, "Successfully got handler {}", i);
status[i] = true; status[i] = true;
loaded.emplace(camera_name, handlers[i]);
return handlers[i]; return handlers[i];
} }
} }
@ -161,6 +150,12 @@ void QtMultimediaCameraHandler::ReleaseHandler(
NGLOG_INFO(Service_CAM, "Successfully released handler {}", i); NGLOG_INFO(Service_CAM, "Successfully released handler {}", i);
status[i] = false; status[i] = false;
handlers[i]->started = false; handlers[i]->started = false;
for (auto it = loaded.begin(); it != loaded.end(); it++) {
if (it->second == handlers[i]) {
loaded.erase(it);
break;
}
}
break; break;
} }
} }
@ -178,6 +173,7 @@ void QtMultimediaCameraHandler::CreateCamera(const std::string& camera_name) {
settings.setMinimumFrameRate(30); settings.setMinimumFrameRate(30);
settings.setMaximumFrameRate(30); settings.setMaximumFrameRate(30);
camera->setViewfinder(&camera_surface); camera->setViewfinder(&camera_surface);
camera->load();
} }
void QtMultimediaCameraHandler::StopCamera() { void QtMultimediaCameraHandler::StopCamera() {

View File

@ -6,6 +6,7 @@
#include <array> #include <array>
#include <string> #include <string>
#include <unordered_map>
#include <vector> #include <vector>
#include <QAbstractVideoSurface> #include <QAbstractVideoSurface>
#include <QCamera> #include <QCamera>
@ -13,7 +14,7 @@
#include <QImage> #include <QImage>
#include <QMutex> #include <QMutex>
#include "citra_qt/camera/camera_util.h" #include "citra_qt/camera/camera_util.h"
#include "citra_qt/camera/qt_camera_factory.h" #include "citra_qt/camera/qt_camera_base.h"
#include "core/frontend/camera/interface.h" #include "core/frontend/camera/interface.h"
class GMainWindow; class GMainWindow;
@ -36,30 +37,24 @@ private:
class QtMultimediaCameraHandler; class QtMultimediaCameraHandler;
/// This class is only an interface. It just calls QtMultimediaCameraHandler. /// This class is only an interface. It just calls QtMultimediaCameraHandler.
class QtMultimediaCamera final : public CameraInterface { class QtMultimediaCamera final : public QtCameraInterface {
public: public:
QtMultimediaCamera(const std::string& camera_name); QtMultimediaCamera(const std::string& camera_name, const Service::CAM::Flip& flip);
~QtMultimediaCamera(); ~QtMultimediaCamera();
void StartCapture() override; void StartCapture() override;
void StopCapture() override; void StopCapture() override;
void SetResolution(const Service::CAM::Resolution&) override;
void SetFlip(Service::CAM::Flip) override;
void SetEffect(Service::CAM::Effect) override;
void SetFormat(Service::CAM::OutputFormat) override;
void SetFrameRate(Service::CAM::FrameRate frame_rate) override; void SetFrameRate(Service::CAM::FrameRate frame_rate) override;
std::vector<u16> ReceiveFrame() override; QImage QtReceiveFrame() override;
bool IsPreviewAvailable() override; bool IsPreviewAvailable() override;
private: private:
std::shared_ptr<QtMultimediaCameraHandler> handler; std::shared_ptr<QtMultimediaCameraHandler> handler;
int width, height;
bool output_rgb;
bool flip_horizontal, flip_vertical;
}; };
class QtMultimediaCameraFactory final : public QtCameraFactory { class QtMultimediaCameraFactory final : public QtCameraFactory {
public: public:
std::unique_ptr<CameraInterface> Create(const std::string& config) const override; std::unique_ptr<CameraInterface> Create(const std::string& config,
const Service::CAM::Flip& flip) override;
}; };
class QtMultimediaCameraHandler final : public QObject { class QtMultimediaCameraHandler final : public QObject {
@ -68,7 +63,7 @@ class QtMultimediaCameraHandler final : public QObject {
public: public:
/// Creates the global handler. Must be called in UI thread. /// Creates the global handler. Must be called in UI thread.
static void Init(); static void Init();
static std::shared_ptr<QtMultimediaCameraHandler> GetHandler(); static std::shared_ptr<QtMultimediaCameraHandler> GetHandler(const std::string& camera_name);
static void ReleaseHandler(const std::shared_ptr<QtMultimediaCameraHandler>& handler); static void ReleaseHandler(const std::shared_ptr<QtMultimediaCameraHandler>& handler);
/** /**
@ -98,6 +93,7 @@ private:
static std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> handlers; static std::array<std::shared_ptr<QtMultimediaCameraHandler>, 3> handlers;
static std::array<bool, 3> status; static std::array<bool, 3> status;
static std::unordered_map<std::string, std::shared_ptr<QtMultimediaCameraHandler>> loaded;
friend class QtMultimediaCamera; // For access to camera_surface (and camera) friend class QtMultimediaCamera; // For access to camera_surface (and camera)
}; };

View File

@ -5,47 +5,36 @@
#include <QFileDialog> #include <QFileDialog>
#include <QImageReader> #include <QImageReader>
#include <QMessageBox> #include <QMessageBox>
#include <QThread>
#include "citra_qt/camera/still_image_camera.h" #include "citra_qt/camera/still_image_camera.h"
namespace Camera { namespace Camera {
StillImageCamera::StillImageCamera(QImage image_) : image(std::move(image_)) {} StillImageCamera::StillImageCamera(QImage image_, const Service::CAM::Flip& flip)
: QtCameraInterface(flip), image(std::move(image_)) {}
StillImageCamera::~StillImageCamera() {
StillImageCameraFactory::last_path.clear();
}
void StillImageCamera::StartCapture() {} void StillImageCamera::StartCapture() {}
void StillImageCamera::StopCapture() {} void StillImageCamera::StopCapture() {}
void StillImageCamera::SetFormat(Service::CAM::OutputFormat output_format) { QImage StillImageCamera::QtReceiveFrame() {
output_rgb = output_format == Service::CAM::OutputFormat::RGB565; return image;
}
void StillImageCamera::SetResolution(const Service::CAM::Resolution& resolution) {
width = resolution.width;
height = resolution.height;
}
void StillImageCamera::SetFlip(Service::CAM::Flip flip) {
using namespace Service::CAM;
flip_horizontal = (flip == Flip::Horizontal) || (flip == Flip::Reverse);
flip_vertical = (flip == Flip::Vertical) || (flip == Flip::Reverse);
}
void StillImageCamera::SetEffect(Service::CAM::Effect effect) {
if (effect != Service::CAM::Effect::None) {
NGLOG_ERROR(Service_CAM, "Unimplemented effect {}", static_cast<int>(effect));
}
}
std::vector<u16> StillImageCamera::ReceiveFrame() {
return CameraUtil::ProcessImage(image, width, height, output_rgb, flip_horizontal,
flip_vertical);
} }
bool StillImageCamera::IsPreviewAvailable() { bool StillImageCamera::IsPreviewAvailable() {
return !image.isNull(); return !image.isNull();
} }
const std::string StillImageCameraFactory::getFilePath() { std::string StillImageCameraFactory::last_path;
const std::string StillImageCameraFactory::GetFilePath() const {
if (!last_path.empty()) {
return last_path;
}
QList<QByteArray> types = QImageReader::supportedImageFormats(); QList<QByteArray> types = QImageReader::supportedImageFormats();
QList<QString> temp_filters; QList<QString> temp_filters;
for (QByteArray type : types) { for (QByteArray type : types) {
@ -53,21 +42,29 @@ const std::string StillImageCameraFactory::getFilePath() {
} }
QString filter = QObject::tr("Supported image files (%1)").arg(temp_filters.join(" ")); QString filter = QObject::tr("Supported image files (%1)").arg(temp_filters.join(" "));
last_path =
return QFileDialog::getOpenFileName(nullptr, QObject::tr("Open File"), ".", filter) QFileDialog::getOpenFileName(nullptr, QObject::tr("Open File"), ".", filter).toStdString();
.toStdString(); return last_path;
} }
std::unique_ptr<CameraInterface> StillImageCameraFactory::Create(const std::string& config) const { std::unique_ptr<CameraInterface> StillImageCameraFactory::Create(const std::string& config,
const Service::CAM::Flip& flip) {
std::string real_config = config; std::string real_config = config;
if (config.empty()) { if (config.empty()) {
real_config = getFilePath(); // call GetFilePath() in UI thread (note: StillImageCameraFactory itself is initialized in
// UI thread, so we can just pass in "this" here)
if (thread() == QThread::currentThread()) {
real_config = GetFilePath();
} else {
QMetaObject::invokeMethod(this, "GetFilePath", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(std::string, real_config));
}
} }
QImage image(QString::fromStdString(real_config)); QImage image(QString::fromStdString(real_config));
if (image.isNull()) { if (image.isNull()) {
NGLOG_ERROR(Service_CAM, "Couldn't load image \"{}\"", real_config.c_str()); NGLOG_ERROR(Service_CAM, "Couldn't load image \"{}\"", real_config.c_str());
} }
return std::make_unique<StillImageCamera>(image); return std::make_unique<StillImageCamera>(image, flip);
} }
} // namespace Camera } // namespace Camera

View File

@ -7,37 +7,39 @@
#include <vector> #include <vector>
#include <QImage> #include <QImage>
#include "citra_qt/camera/camera_util.h" #include "citra_qt/camera/camera_util.h"
#include "citra_qt/camera/qt_camera_factory.h" #include "citra_qt/camera/qt_camera_base.h"
#include "core/frontend/camera/interface.h" #include "core/frontend/camera/interface.h"
namespace Camera { namespace Camera {
class StillImageCamera final : public CameraInterface { class StillImageCamera final : public QtCameraInterface {
public: public:
StillImageCamera(QImage image); StillImageCamera(QImage image, const Service::CAM::Flip& flip);
~StillImageCamera();
void StartCapture() override; void StartCapture() override;
void StopCapture() override; void StopCapture() override;
void SetResolution(const Service::CAM::Resolution&) override;
void SetFlip(Service::CAM::Flip) override;
void SetEffect(Service::CAM::Effect) override;
void SetFormat(Service::CAM::OutputFormat) override;
void SetFrameRate(Service::CAM::FrameRate frame_rate) override {} void SetFrameRate(Service::CAM::FrameRate frame_rate) override {}
std::vector<u16> ReceiveFrame() override; QImage QtReceiveFrame() override;
bool IsPreviewAvailable() override; bool IsPreviewAvailable() override;
private: private:
QImage image; QImage image;
int width, height;
bool output_rgb;
bool flip_horizontal, flip_vertical;
}; };
class StillImageCameraFactory final : public QtCameraFactory { class StillImageCameraFactory final : public QObject, public QtCameraFactory {
Q_OBJECT
public: public:
std::unique_ptr<CameraInterface> Create(const std::string& config) const override; std::unique_ptr<CameraInterface> Create(const std::string& config,
const Service::CAM::Flip& flip) override;
Q_INVOKABLE const std::string GetFilePath() const;
private: private:
static const std::string getFilePath(); /// Record the path chosen to avoid multiple prompt problem
static std::string last_path;
friend class StillImageCamera;
}; };
} // namespace Camera } // namespace Camera

View File

@ -128,14 +128,19 @@ void Config::ReadValues() {
qt_config->value("camera_outer_right_name", "blank").toString().toStdString(); qt_config->value("camera_outer_right_name", "blank").toString().toStdString();
Settings::values.camera_config[OuterRightCamera] = Settings::values.camera_config[OuterRightCamera] =
qt_config->value("camera_outer_right_config", "").toString().toStdString(); qt_config->value("camera_outer_right_config", "").toString().toStdString();
Settings::values.camera_flip[OuterRightCamera] =
qt_config->value("camera_outer_right_flip", "0").toInt();
Settings::values.camera_name[InnerCamera] = Settings::values.camera_name[InnerCamera] =
qt_config->value("camera_inner_name", "blank").toString().toStdString(); qt_config->value("camera_inner_name", "blank").toString().toStdString();
Settings::values.camera_config[InnerCamera] = Settings::values.camera_config[InnerCamera] =
qt_config->value("camera_inner_config", "").toString().toStdString(); qt_config->value("camera_inner_config", "").toString().toStdString();
Settings::values.camera_flip[InnerCamera] = qt_config->value("camera_inner_flip", "").toInt();
Settings::values.camera_name[OuterLeftCamera] = Settings::values.camera_name[OuterLeftCamera] =
qt_config->value("camera_outer_left_name", "blank").toString().toStdString(); qt_config->value("camera_outer_left_name", "blank").toString().toStdString();
Settings::values.camera_config[OuterLeftCamera] = Settings::values.camera_config[OuterLeftCamera] =
qt_config->value("camera_outer_left_config", "").toString().toStdString(); qt_config->value("camera_outer_left_config", "").toString().toStdString();
Settings::values.camera_flip[OuterLeftCamera] =
qt_config->value("camera_outer_left_flip", "").toInt();
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Data Storage"); qt_config->beginGroup("Data Storage");
@ -317,14 +322,17 @@ void Config::SaveValues() {
QString::fromStdString(Settings::values.camera_name[OuterRightCamera])); QString::fromStdString(Settings::values.camera_name[OuterRightCamera]));
qt_config->setValue("camera_outer_right_config", qt_config->setValue("camera_outer_right_config",
QString::fromStdString(Settings::values.camera_config[OuterRightCamera])); QString::fromStdString(Settings::values.camera_config[OuterRightCamera]));
qt_config->setValue("camera_outer_right_flip", Settings::values.camera_flip[OuterRightCamera]);
qt_config->setValue("camera_inner_name", qt_config->setValue("camera_inner_name",
QString::fromStdString(Settings::values.camera_name[InnerCamera])); QString::fromStdString(Settings::values.camera_name[InnerCamera]));
qt_config->setValue("camera_inner_config", qt_config->setValue("camera_inner_config",
QString::fromStdString(Settings::values.camera_config[InnerCamera])); QString::fromStdString(Settings::values.camera_config[InnerCamera]));
qt_config->setValue("camera_inner_flip", Settings::values.camera_flip[InnerCamera]);
qt_config->setValue("camera_outer_left_name", qt_config->setValue("camera_outer_left_name",
QString::fromStdString(Settings::values.camera_name[OuterLeftCamera])); QString::fromStdString(Settings::values.camera_name[OuterLeftCamera]));
qt_config->setValue("camera_outer_left_config", qt_config->setValue("camera_outer_left_config",
QString::fromStdString(Settings::values.camera_config[OuterLeftCamera])); QString::fromStdString(Settings::values.camera_config[OuterLeftCamera]));
qt_config->setValue("camera_outer_left_flip", Settings::values.camera_flip[OuterLeftCamera]);
qt_config->endGroup(); qt_config->endGroup();
qt_config->beginGroup("Data Storage"); qt_config->beginGroup("Data Storage");

View File

@ -27,14 +27,7 @@ ConfigureCamera::ConfigureCamera(QWidget* parent)
// Load settings // Load settings
camera_name = Settings::values.camera_name; camera_name = Settings::values.camera_name;
camera_config = Settings::values.camera_config; camera_config = Settings::values.camera_config;
for (auto&& item : camera_name) { camera_flip = Settings::values.camera_flip;
if (item == "opencv") {
QMessageBox::critical(this, tr("Error"),
tr("Sorry, Citra has removed support for OpenCV cameras.\n\nYour "
"existing OpenCV cameras have been replaced with Blank."));
item = "blank";
}
}
QList<QCameraInfo> cameras = QCameraInfo::availableCameras(); QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
for (const QCameraInfo& cameraInfo : cameras) { for (const QCameraInfo& cameraInfo : cameras) {
ui->system_camera->addItem(cameraInfo.deviceName()); ui->system_camera->addItem(cameraInfo.deviceName());
@ -98,6 +91,8 @@ void ConfigureCamera::connectEvents() {
connect(ui->system_camera, connect(ui->system_camera,
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
[=] { stopPreviewing(); }); [=] { stopPreviewing(); });
connect(ui->camera_flip, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, [=] { stopPreviewing(); });
} }
void ConfigureCamera::updateCameraMode() { void ConfigureCamera::updateCameraMode() {
@ -148,6 +143,8 @@ void ConfigureCamera::updateImageSourceUI() {
} }
ui->system_camera_label->setHidden(image_source != 2); ui->system_camera_label->setHidden(image_source != 2);
ui->system_camera->setHidden(image_source != 2); ui->system_camera->setHidden(image_source != 2);
ui->camera_flip_label->setHidden(image_source == 0);
ui->camera_flip->setHidden(image_source == 0);
} }
void ConfigureCamera::recordConfig() { void ConfigureCamera::recordConfig() {
@ -166,10 +163,12 @@ void ConfigureCamera::recordConfig() {
if (current_selected == CameraPosition::RearBoth) { if (current_selected == CameraPosition::RearBoth) {
camera_name[0] = camera_name[2] = implementation; camera_name[0] = camera_name[2] = implementation;
camera_config[0] = camera_config[2] = config; camera_config[0] = camera_config[2] = config;
camera_flip[0] = camera_flip[2] = ui->camera_flip->currentIndex();
} else if (current_selected != CameraPosition::Null) { } else if (current_selected != CameraPosition::Null) {
int index = static_cast<int>(current_selected); int index = static_cast<int>(current_selected);
camera_name[index] = implementation; camera_name[index] = implementation;
camera_config[index] = config; camera_config[index] = config;
camera_flip[index] = ui->camera_flip->currentIndex();
} }
current_selected = getCameraSelection(); current_selected = getCameraSelection();
} }
@ -187,9 +186,9 @@ void ConfigureCamera::startPreviewing() {
ui->preview_box->setToolTip(tr("Resolution: ") + QString::number(preview_width) + "*" + ui->preview_box->setToolTip(tr("Resolution: ") + QString::number(preview_width) + "*" +
QString::number(preview_height)); QString::number(preview_height));
// Load previewing camera // Load previewing camera
previewing_camera = previewing_camera = Camera::CreateCameraPreview(
Camera::CreateCameraPreview(camera_name[camera_selection], camera_config[camera_selection], camera_name[camera_selection], camera_config[camera_selection], preview_width,
preview_width, preview_height); preview_height, static_cast<Service::CAM::Flip>(camera_flip[camera_selection]));
if (!previewing_camera) { if (!previewing_camera) {
stopPreviewing(); stopPreviewing();
return; return;
@ -262,6 +261,7 @@ void ConfigureCamera::setConfiguration() {
} else { } else {
ui->camera_file->setText(QString::fromStdString(camera_config[index])); ui->camera_file->setText(QString::fromStdString(camera_config[index]));
} }
ui->camera_flip->setCurrentIndex(camera_flip[index]);
updateImageSourceUI(); updateImageSourceUI();
} }
@ -288,6 +288,7 @@ void ConfigureCamera::applyConfiguration() {
stopPreviewing(); stopPreviewing();
Settings::values.camera_name = camera_name; Settings::values.camera_name = camera_name;
Settings::values.camera_config = camera_config; Settings::values.camera_config = camera_config;
Settings::values.camera_flip = camera_flip;
Settings::Apply(); Settings::Apply();
} }

View File

@ -47,6 +47,7 @@ private:
std::unique_ptr<Ui::ConfigureCamera> ui; std::unique_ptr<Ui::ConfigureCamera> ui;
std::array<std::string, 3> camera_name; std::array<std::string, 3> camera_name;
std::array<std::string, 3> camera_config; std::array<std::string, 3> camera_config;
std::array<int, 3> camera_flip;
int timer_id = 0; int timer_id = 0;
int preview_width = 0; int preview_width = 0;
int preview_height = 0; int preview_height = 0;

View File

@ -1,257 +1,347 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>ConfigureCamera</class> <class>ConfigureCamera</class>
<widget class="QWidget" name="ConfigureCamera"> <widget class="QWidget" name="ConfigureCamera">
<layout class="QVBoxLayout" name="verticalLayout"> <property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Camera</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QGroupBox" name="groupBox"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="title"> <item>
<string>Camera</string> <widget class="QLabel" name="camera_selection_label">
<property name="toolTip">
<string>Select the camera to configure</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <property name="text">
<item> <string>Camera to configure:</string>
<layout class="QHBoxLayout" name="horizontalLayout"> </property>
<item> </widget>
<widget class="QLabel" name="camera_selection_label"> </item>
<property name="toolTip"> <item>
<string>Select the camera to configure</string> <widget class="QComboBox" name="camera_selection">
</property> <property name="toolTip">
<property name="text"> <string>Select the camera to configure</string>
<string>Camera to configure:</string> </property>
</property> <item>
</widget> <property name="text">
</item> <string>Front</string>
<item> </property>
<widget class="QComboBox" name="camera_selection"> </item>
<property name="toolTip"> <item>
<string>Select the camera to configure</string> <property name="text">
</property> <string>Rear</string>
<item> </property>
<property name="text"> </item>
<string>Front</string> </widget>
</property> </item>
</item> </layout>
<item>
<property name="text">
<string>Rear</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="camera_mode_label">
<property name="toolTip">
<string>Select the camera mode (single or double)</string>
</property>
<property name="text">
<string>Camera mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="camera_mode">
<property name="toolTip">
<string>Select the camera mode (single or double)</string>
</property>
<item>
<property name="text">
<string>Single (2D)</string>
</property>
</item>
<item>
<property name="text">
<string>Double (3D)</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="camera_position_label">
<property name="toolTip">
<string>Select the position of camera to configure</string>
</property>
<property name="text">
<string>Camera position:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="camera_position">
<property name="toolTip">
<string>Select the position of camera to configure</string>
</property>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="configurationBox"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="title"> <item>
<string>Configuration</string> <widget class="QLabel" name="camera_mode_label">
<property name="toolTip">
<string>Select the camera mode (single or double)</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_configuration"> <property name="text">
<item> <string>Camera mode:</string>
<layout class="QHBoxLayout" name="horizontalLayout_4"> </property>
<item> </widget>
<widget class="QLabel" name="image_source_label"> </item>
<property name="toolTip"> <item>
<string>Select where the image of the emulated camera comes from. It may be an image or a real camera.</string> <widget class="QComboBox" name="camera_mode">
</property> <property name="toolTip">
<property name="text"> <string>Select the camera mode (single or double)</string>
<string>Camera Image Source:</string> </property>
</property> <item>
</widget> <property name="text">
</item> <string>Single (2D)</string>
<item> </property>
<widget class="QComboBox" name="image_source"> </item>
<property name="toolTip"> <item>
<string>Select where the image of the emulated camera come from. It may be an image or a real camera.</string> <property name="text">
</property> <string>Double (3D)</string>
<item> </property>
<property name="text"> </item>
<string>Blank (blank)</string> </widget>
</property> </item>
</item> </layout>
<item>
<property name="text">
<string>Still Image (image)</string>
</property>
</item>
<item>
<property name="text">
<string>System Camera (qt)</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="camera_file_label">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>File:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="camera_file"/>
</item>
<item>
<widget class="QToolButton" name="toolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="system_camera_label">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>Camera:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="system_camera">
<item>
<property name="text">
<string>&lt;Default&gt;</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="prompt_before_load">
<property name="text">
<string>Prompt before load</string>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
<item> <item>
<widget class="QGroupBox" name="previewBox"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="title"> <item>
<string>Preview</string> <widget class="QLabel" name="camera_position_label">
<property name="toolTip">
<string>Select the position of camera to configure</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <property name="text">
<item> <string>Camera position:</string>
<widget class="QLabel" name="preview_box"> </property>
<property name="baseSize"> </widget>
<size> </item>
<width>512</width> <item>
<height>384</height> <widget class="QComboBox" name="camera_position">
</size> <property name="toolTip">
</property> <string>Select the position of camera to configure</string>
<property name="toolTip"> </property>
<string>Resolution: 512*384</string> <item>
</property> <property name="text">
<property name="text"> <string>Left</string>
<string/> </property>
</property> </item>
</widget> <item>
</item> <property name="text">
<item> <string>Right</string>
<widget class="QPushButton" name="preview_button"> </property>
<property name="text"> </item>
<string>Click to preview</string> </widget>
</property> </item>
</widget> </layout>
</item> </item>
</layout> </layout>
</widget> </widget>
</item>
<item>
<widget class="QGroupBox" name="configurationBox">
<property name="title">
<string>Configuration</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_configuration">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="image_source_label">
<property name="toolTip">
<string>Select where the image of the emulated camera comes from. It may be an image or a real camera.</string>
</property>
<property name="text">
<string>Camera Image Source:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="image_source">
<property name="toolTip">
<string>Select where the image of the emulated camera comes from. It may be an image or a real camera.</string>
</property>
<item>
<property name="text">
<string>Blank (blank)</string>
</property>
</item>
<item>
<property name="text">
<string>Still Image (image)</string>
</property>
</item>
<item>
<property name="text">
<string>System Camera (qt)</string>
</property>
</item>
</widget>
</item>
</layout>
</item> </item>
<item> <item>
<spacer> <layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="orientation"> <item>
<enum>Qt::Vertical</enum> <widget class="QLabel" name="camera_file_label">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="text">
<size> <string>File:</string>
<width>20</width>
<height>40</height>
</size>
</property> </property>
</spacer> </widget>
</item>
<item>
<widget class="QLineEdit" name="camera_file"/>
</item>
<item>
<widget class="QToolButton" name="toolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> <item>
</widget> <layout class="QHBoxLayout" name="horizontalLayout_7">
<resources/> <item>
<connections/> <widget class="QLabel" name="system_camera_label">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="toolTip">
<string>Select the system camera to use</string>
</property>
<property name="text">
<string>Camera:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="system_camera">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>250</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select the system camera to use</string>
</property>
<item>
<property name="text">
<string>&lt;Default&gt;</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="camera_flip_label">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="toolTip">
<string>Select the image flip to apply</string>
</property>
<property name="text">
<string>Flip:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="camera_flip">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>800</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Select the image flip to apply</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Horizontal</string>
</property>
</item>
<item>
<property name="text">
<string>Vertical</string>
</property>
</item>
<item>
<property name="text">
<string>Reverse</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="prompt_before_load">
<property name="toolTip">
<string>Select an image file every time before the camera is loaded</string>
</property>
<property name="text">
<string>Prompt before load</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="previewBox">
<property name="title">
<string>Preview</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="preview_box">
<property name="baseSize">
<size>
<width>512</width>
<height>384</height>
</size>
</property>
<property name="toolTip">
<string>Resolution: 512*384</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="preview_button">
<property name="text">
<string>Click to preview</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui> </ui>

View File

@ -173,14 +173,20 @@ public:
&extension); &extension);
QString title = data(TitleRole).toString(); QString title = data(TitleRole).toString();
QString second_name = QString::fromStdString(filename + extension); QString second_name = QString::fromStdString(filename + extension);
static QRegExp installed_system_pattern( static QRegExp installed_pattern(
QString::fromStdString( QString::fromStdString(
FileUtil::GetUserPath(D_SDMC_IDX) + FileUtil::GetUserPath(D_SDMC_IDX) +
"Nintendo " "Nintendo "
"3DS/00000000000000000000000000000000/00000000000000000000000000000000/" "3DS/00000000000000000000000000000000/00000000000000000000000000000000/"
"title/000400(0|1)0/[0-9a-f]{8}/content/") "title/0004000(0|e)/[0-9a-f]{8}/content/")
.replace("\\", "\\\\")); .replace("\\", "\\\\"));
if (installed_system_pattern.exactMatch(QString::fromStdString(path))) { static QRegExp system_pattern(
QString::fromStdString(FileUtil::GetUserPath(D_NAND_IDX) +
"00000000000000000000000000000000/"
"title/00040010/[0-9a-f]{8}/content/")
.replace("\\", "\\\\"));
if (installed_pattern.exactMatch(QString::fromStdString(path)) ||
system_pattern.exactMatch(QString::fromStdString(path))) {
// Use a different mechanism for system / installed titles showing program ID // Use a different mechanism for system / installed titles showing program ID
second_name = QString("%1-%2") second_name = QString("%1-%2")
.arg(data(ProgramIdRole).toULongLong(), 16, 16, QChar('0')) .arg(data(ProgramIdRole).toULongLong(), 16, 16, QChar('0'))

View File

@ -3866,6 +3866,8 @@ SWI_INST : {
num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs; num_instrs >= cpu->NumInstrsToExecute ? 0 : cpu->NumInstrsToExecute - num_instrs;
num_instrs = 0; num_instrs = 0;
Kernel::CallSVC(inst_cream->num & 0xFFFF); Kernel::CallSVC(inst_cream->num & 0xFFFF);
// The kernel would call ERET to get here, which clears exclusive memory state.
cpu->UnsetExclusiveMemoryAddress();
} }
cpu->Reg[15] += cpu->GetInstructionSize(); cpu->Reg[15] += cpu->GetInstructionSize();

View File

@ -17,28 +17,29 @@ void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> fac
factories[name] = std::move(factory); factories[name] = std::move(factory);
} }
std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config) { std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config,
const Service::CAM::Flip& flip) {
auto pair = factories.find(name); auto pair = factories.find(name);
if (pair != factories.end()) { if (pair != factories.end()) {
return pair->second->Create(config); return pair->second->Create(config, flip);
} }
if (name != "blank") { if (name != "blank") {
NGLOG_ERROR(Service_CAM, "Unknown camera \"{}\"", name); NGLOG_ERROR(Service_CAM, "Unknown camera {}", name);
} }
return std::make_unique<BlankCamera>(); return std::make_unique<BlankCamera>();
} }
std::unique_ptr<CameraInterface> CreateCameraPreview(const std::string& name, std::unique_ptr<CameraInterface> CreateCameraPreview(const std::string& name,
const std::string& config, int width, const std::string& config, int width,
int height) { int height, const Service::CAM::Flip& flip) {
auto pair = factories.find(name); auto pair = factories.find(name);
if (pair != factories.end()) { if (pair != factories.end()) {
return pair->second->CreatePreview(config, width, height); return pair->second->CreatePreview(config, width, height, flip);
} }
if (name != "blank") { if (name != "blank") {
NGLOG_ERROR(Service_CAM, "Unknown camera \"{}\"", name); NGLOG_ERROR(Service_CAM, "Unknown camera {}", name);
} }
return std::make_unique<BlankCamera>(); return std::make_unique<BlankCamera>();
} }

View File

@ -18,22 +18,26 @@ public:
* Creates a camera object based on the configuration string. * Creates a camera object based on the configuration string.
* @param config Configuration string to create the camera. The implementation can decide the * @param config Configuration string to create the camera. The implementation can decide the
* meaning of this string. * meaning of this string.
* @param flip The image flip to apply
* @returns a unique_ptr to the created camera object. * @returns a unique_ptr to the created camera object.
*/ */
virtual std::unique_ptr<CameraInterface> Create(const std::string& config) const = 0; virtual std::unique_ptr<CameraInterface> Create(const std::string& config,
const Service::CAM::Flip& flip) = 0;
/** /**
* Creates a camera object for preview based on the configuration string. * Creates a camera object for preview based on the configuration string.
* @param config Configuration string to create the camera. The implementation can decide the * @param config Configuration string to create the camera. The implementation can decide the
* meaning of this string. * meaning of this string.
* @param flip The image flip to apply
* @returns a unique_ptr to the created camera object. * @returns a unique_ptr to the created camera object.
* Note: The default implementation for this is to call Create(). Derived classes may have other * Note: The default implementation for this is to call Create(). Derived classes may have other
* Implementations. For example, A dialog may be used instead of NGLOG_ERROR when error * Implementations. For example, A dialog may be used instead of NGLOG_ERROR when error
* occurs. * occurs.
*/ */
virtual std::unique_ptr<CameraInterface> CreatePreview(const std::string& config, int width, virtual std::unique_ptr<CameraInterface> CreatePreview(const std::string& config, int width,
int height) const { int height,
return Create(config); const Service::CAM::Flip& flip) {
return Create(config, flip);
} }
}; };
@ -50,7 +54,8 @@ void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> fac
* @param config Configuration string to create the camera. The meaning of this string is * @param config Configuration string to create the camera. The meaning of this string is
* defined by the factory. * defined by the factory.
*/ */
std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config); std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config,
const Service::CAM::Flip& flip);
/** /**
* Creates a camera from the factory for previewing. * Creates a camera from the factory for previewing.
@ -60,6 +65,6 @@ std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std
*/ */
std::unique_ptr<CameraInterface> CreateCameraPreview(const std::string& name, std::unique_ptr<CameraInterface> CreateCameraPreview(const std::string& name,
const std::string& config, int width, const std::string& config, int width,
int height); int height, const Service::CAM::Flip& flip);
} // namespace Camera } // namespace Camera

View File

@ -404,7 +404,7 @@ void ReceiveProperty(Service::Interface* self) {
cmd_buff[0] = IPC::MakeHeader(0x16, 0x2, 0x2); cmd_buff[0] = IPC::MakeHeader(0x16, 0x2, 0x2);
cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 0; // stub 0 (32 bit value) cmd_buff[2] = buff_size; // Should be actual number of read bytes.
cmd_buff[3] = (buff_size << 4 | 0xC); cmd_buff[3] = (buff_size << 4 | 0xC);
cmd_buff[4] = buff_addr; cmd_buff[4] = buff_addr;

View File

@ -1041,8 +1041,9 @@ void Module::ReloadCameraDevices() {
} }
void Module::LoadCameraImplementation(CameraConfig& camera, int camera_id) { void Module::LoadCameraImplementation(CameraConfig& camera, int camera_id) {
camera.impl = Camera::CreateCamera(Settings::values.camera_name[camera_id], camera.impl = Camera::CreateCamera(
Settings::values.camera_config[camera_id]); Settings::values.camera_name[camera_id], Settings::values.camera_config[camera_id],
static_cast<Service::CAM::Flip>(Settings::values.camera_flip[camera_id]));
camera.impl->SetFlip(camera.contexts[0].flip); camera.impl->SetFlip(camera.contexts[0].flip);
camera.impl->SetEffect(camera.contexts[0].effect); camera.impl->SetEffect(camera.contexts[0].effect);
camera.impl->SetFormat(camera.contexts[0].format); camera.impl->SetFormat(camera.contexts[0].format);

View File

@ -140,6 +140,7 @@ struct Values {
// Camera // Camera
std::array<std::string, Service::CAM::NumCameras> camera_name; std::array<std::string, Service::CAM::NumCameras> camera_name;
std::array<std::string, Service::CAM::NumCameras> camera_config; std::array<std::string, Service::CAM::NumCameras> camera_config;
std::array<int, Service::CAM::NumCameras> camera_flip;
// Debugging // Debugging
bool use_gdbstub; bool use_gdbstub;

View File

@ -33,70 +33,70 @@ namespace Shader {
typedef void (JitShader::*JitFunction)(Instruction instr); typedef void (JitShader::*JitFunction)(Instruction instr);
const JitFunction instr_table[64] = { const JitFunction instr_table[64] = {
&JitShader::Compile_ADD, // add &JitShader::Compile_ADD, // add
&JitShader::Compile_DP3, // dp3 &JitShader::Compile_DP3, // dp3
&JitShader::Compile_DP4, // dp4 &JitShader::Compile_DP4, // dp4
&JitShader::Compile_DPH, // dph &JitShader::Compile_DPH, // dph
nullptr, // unknown nullptr, // unknown
&JitShader::Compile_EX2, // ex2 &JitShader::Compile_EX2, // ex2
&JitShader::Compile_LG2, // lg2 &JitShader::Compile_LG2, // lg2
nullptr, // unknown nullptr, // unknown
&JitShader::Compile_MUL, // mul &JitShader::Compile_MUL, // mul
&JitShader::Compile_SGE, // sge &JitShader::Compile_SGE, // sge
&JitShader::Compile_SLT, // slt &JitShader::Compile_SLT, // slt
&JitShader::Compile_FLR, // flr &JitShader::Compile_FLR, // flr
&JitShader::Compile_MAX, // max &JitShader::Compile_MAX, // max
&JitShader::Compile_MIN, // min &JitShader::Compile_MIN, // min
&JitShader::Compile_RCP, // rcp &JitShader::Compile_RCP, // rcp
&JitShader::Compile_RSQ, // rsq &JitShader::Compile_RSQ, // rsq
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
&JitShader::Compile_MOVA, // mova &JitShader::Compile_MOVA, // mova
&JitShader::Compile_MOV, // mov &JitShader::Compile_MOV, // mov
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
&JitShader::Compile_DPH, // dphi &JitShader::Compile_DPH, // dphi
nullptr, // unknown nullptr, // unknown
&JitShader::Compile_SGE, // sgei &JitShader::Compile_SGE, // sgei
&JitShader::Compile_SLT, // slti &JitShader::Compile_SLT, // slti
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
nullptr, // unknown nullptr, // unknown
&JitShader::Compile_NOP, // nop &JitShader::Compile_NOP, // nop
&JitShader::Compile_END, // end &JitShader::Compile_END, // end
nullptr, // break &JitShader::Compile_BREAKC, // breakc
&JitShader::Compile_CALL, // call &JitShader::Compile_CALL, // call
&JitShader::Compile_CALLC, // callc &JitShader::Compile_CALLC, // callc
&JitShader::Compile_CALLU, // callu &JitShader::Compile_CALLU, // callu
&JitShader::Compile_IF, // ifu &JitShader::Compile_IF, // ifu
&JitShader::Compile_IF, // ifc &JitShader::Compile_IF, // ifc
&JitShader::Compile_LOOP, // loop &JitShader::Compile_LOOP, // loop
&JitShader::Compile_EMIT, // emit &JitShader::Compile_EMIT, // emit
&JitShader::Compile_SETE, // sete &JitShader::Compile_SETE, // sete
&JitShader::Compile_JMP, // jmpc &JitShader::Compile_JMP, // jmpc
&JitShader::Compile_JMP, // jmpu &JitShader::Compile_JMP, // jmpu
&JitShader::Compile_CMP, // cmp &JitShader::Compile_CMP, // cmp
&JitShader::Compile_CMP, // cmp &JitShader::Compile_CMP, // cmp
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // madi &JitShader::Compile_MAD, // madi
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
&JitShader::Compile_MAD, // mad &JitShader::Compile_MAD, // mad
}; };
// The following is used to alias some commonly used registers. Generally, RAX-RDX and XMM0-XMM3 can // The following is used to alias some commonly used registers. Generally, RAX-RDX and XMM0-XMM3 can
@ -580,10 +580,30 @@ void JitShader::Compile_RSQ(Instruction instr) {
void JitShader::Compile_NOP(Instruction instr) {} void JitShader::Compile_NOP(Instruction instr) {}
void JitShader::Compile_END(Instruction instr) { void JitShader::Compile_END(Instruction instr) {
// Save conditional code
mov(byte[STATE + offsetof(UnitState, conditional_code[0])], COND0.cvt8());
mov(byte[STATE + offsetof(UnitState, conditional_code[1])], COND1.cvt8());
// Save address/loop registers
sar(ADDROFFS_REG_0, 4);
sar(ADDROFFS_REG_1, 4);
sar(LOOPCOUNT_REG, 4);
mov(dword[STATE + offsetof(UnitState, address_registers[0])], ADDROFFS_REG_0.cvt32());
mov(dword[STATE + offsetof(UnitState, address_registers[1])], ADDROFFS_REG_1.cvt32());
mov(dword[STATE + offsetof(UnitState, address_registers[2])], LOOPCOUNT_REG);
ABI_PopRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8, 16); ABI_PopRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8, 16);
ret(); ret();
} }
void JitShader::Compile_BREAKC(Instruction instr) {
Compile_Assert(looping, "BREAKC must be inside a LOOP");
if (looping) {
Compile_EvaluateCondition(instr);
jnz(*loop_break_label);
}
}
void JitShader::Compile_CALL(Instruction instr) { void JitShader::Compile_CALL(Instruction instr) {
// Push offset of the return // Push offset of the return
push(qword, (instr.flow_control.dest_offset + instr.flow_control.num_instructions)); push(qword, (instr.flow_control.dest_offset + instr.flow_control.num_instructions));
@ -727,11 +747,14 @@ void JitShader::Compile_LOOP(Instruction instr) {
Label l_loop_start; Label l_loop_start;
L(l_loop_start); L(l_loop_start);
loop_break_label = Xbyak::Label();
Compile_Block(instr.flow_control.dest_offset + 1); Compile_Block(instr.flow_control.dest_offset + 1);
add(LOOPCOUNT_REG, LOOPINC); // Increment LOOPCOUNT_REG by Z-component add(LOOPCOUNT_REG, LOOPINC); // Increment LOOPCOUNT_REG by Z-component
sub(LOOPCOUNT, 1); // Increment loop count by 1 sub(LOOPCOUNT, 1); // Increment loop count by 1
jnz(l_loop_start); // Loop if not equal jnz(l_loop_start); // Loop if not equal
L(*loop_break_label);
loop_break_label = boost::none;
looping = false; looping = false;
} }
@ -885,10 +908,17 @@ void JitShader::Compile(const std::array<u32, MAX_PROGRAM_CODE_LENGTH>* program_
mov(UNIFORMS, ABI_PARAM1); mov(UNIFORMS, ABI_PARAM1);
mov(STATE, ABI_PARAM2); mov(STATE, ABI_PARAM2);
// Zero address/loop registers // Load address/loop registers
xor_(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); movsxd(ADDROFFS_REG_0, dword[STATE + offsetof(UnitState, address_registers[0])]);
xor_(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); movsxd(ADDROFFS_REG_1, dword[STATE + offsetof(UnitState, address_registers[1])]);
xor_(LOOPCOUNT_REG, LOOPCOUNT_REG); mov(LOOPCOUNT_REG, dword[STATE + offsetof(UnitState, address_registers[2])]);
shl(ADDROFFS_REG_0, 4);
shl(ADDROFFS_REG_1, 4);
shl(LOOPCOUNT_REG, 4);
// Load conditional code
mov(COND0, byte[STATE + offsetof(UnitState, conditional_code[0])]);
mov(COND1, byte[STATE + offsetof(UnitState, conditional_code[1])]);
// Used to set a register to one // Used to set a register to one
static const __m128 one = {1.f, 1.f, 1.f, 1.f}; static const __m128 one = {1.f, 1.f, 1.f, 1.f};

View File

@ -8,6 +8,7 @@
#include <cstddef> #include <cstddef>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <boost/optional.hpp>
#include <nihstro/shader_bytecode.h> #include <nihstro/shader_bytecode.h>
#include <xbyak.h> #include <xbyak.h>
#include "common/bit_set.h" #include "common/bit_set.h"
@ -58,6 +59,7 @@ public:
void Compile_MOV(Instruction instr); void Compile_MOV(Instruction instr);
void Compile_NOP(Instruction instr); void Compile_NOP(Instruction instr);
void Compile_END(Instruction instr); void Compile_END(Instruction instr);
void Compile_BREAKC(Instruction instr);
void Compile_CALL(Instruction instr); void Compile_CALL(Instruction instr);
void Compile_CALLC(Instruction instr); void Compile_CALLC(Instruction instr);
void Compile_CALLU(Instruction instr); void Compile_CALLU(Instruction instr);
@ -119,6 +121,10 @@ private:
/// Mapping of Pica VS instructions to pointers in the emitted code /// Mapping of Pica VS instructions to pointers in the emitted code
std::array<Xbyak::Label, MAX_PROGRAM_CODE_LENGTH> instruction_labels; std::array<Xbyak::Label, MAX_PROGRAM_CODE_LENGTH> instruction_labels;
/// Label pointing to the end of the current LOOP block. Used by the BREAKC instruction to break
/// out of the loop.
boost::optional<Xbyak::Label> loop_break_label;
/// Offsets in code where a return needs to be inserted /// Offsets in code where a return needs to be inserted
std::vector<unsigned> return_offsets; std::vector<unsigned> return_offsets;