From 8193ebf7d4c451ea412404711681f464b27a871f Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 7 Jun 2023 09:10:40 +0300 Subject: [PATCH 01/30] Remove wxMessageBoxes from non-gui code --- src/Cafe/CafeSystem.cpp | 3 +- src/Cafe/Filesystem/FST/KeyCache.cpp | 8 +- src/Cafe/GraphicPack/GraphicPack2Patches.cpp | 5 +- src/Cafe/HW/Latte/Core/LatteShaderCache.cpp | 35 --------- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 8 +- src/Cafe/HW/MMU/MMU.cpp | 7 +- src/Cafe/OS/libs/nn_save/nn_save.cpp | 76 ------------------- src/config/LaunchSettings.cpp | 6 +- 8 files changed, 8 insertions(+), 140 deletions(-) diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index cd6c2b75..25bb71d6 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -1,5 +1,4 @@ #include "Cafe/OS/common/OSCommon.h" -#include "gui/wxgui.h" #include "Cafe/OS/libs/gx2/GX2.h" #include "Cafe/GameProfile/GameProfile.h" #include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h" @@ -163,7 +162,7 @@ void LoadMainExecutable() applicationRPX = rpl_loadFromMem(rpxData, rpxSize, (char*)_pathToExecutable.c_str()); if (!applicationRPX) { - wxMessageBox(_("Failed to run this title because the executable is damaged")); + cemuLog_log(LogType::Force, "Failed to run this title because the executable is damaged"); cemuLog_createLogFile(false); cemuLog_waitForFlush(); exit(0); diff --git a/src/Cafe/Filesystem/FST/KeyCache.cpp b/src/Cafe/Filesystem/FST/KeyCache.cpp index 5d8d51c1..c69c65ca 100644 --- a/src/Cafe/Filesystem/FST/KeyCache.cpp +++ b/src/Cafe/Filesystem/FST/KeyCache.cpp @@ -1,4 +1,3 @@ -#include #include #include "config/ActiveSettings.h" @@ -74,7 +73,7 @@ void KeyCache_Prepare() } else { - wxMessageBox("Unable to create file keys.txt\nThis can happen if Cemu does not have write permission to it's own directory, the disk is full or if anti-virus software is blocking Cemu.", "Error", wxOK | wxCENTRE | wxICON_ERROR); + cemuLog_log(LogType::Force, "Unable to create file keys.txt\nThis can happen if Cemu does not have write permission to it's own directory, the disk is full or if anti-virus software is blocking Cemu."); } mtxKeyCache.unlock(); return; @@ -107,10 +106,7 @@ void KeyCache_Prepare() continue; if( strishex(line) == false ) { - // show error message - char errorMsg[512]; - sprintf(errorMsg, "Error in keys.txt in line %d\n", lineNumber); - wxMessageBox(errorMsg, "Error", wxOK | wxCENTRE | wxICON_ERROR); + cemuLog_log(LogType::Force, "rror in keys.txt in line {}", lineNumber); continue; } if(line.size() == 32 ) diff --git a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp index 578b55db..162a5536 100644 --- a/src/Cafe/GraphicPack/GraphicPack2Patches.cpp +++ b/src/Cafe/GraphicPack/GraphicPack2Patches.cpp @@ -5,8 +5,6 @@ #include "Cafe/OS/RPL/rpl_structs.h" #include "boost/algorithm/string.hpp" -#include "gui/wxgui.h" // for wxMessageBox - // error handler void PatchErrorHandler::printError(class PatchGroup* patchGroup, sint32 lineNumber, std::string_view errorMsg) { @@ -60,8 +58,7 @@ void PatchErrorHandler::showStageErrorMessageBox() errorMsg.append("\n"); } } - - wxMessageBox(errorMsg, "Graphic pack error"); + cemuLog_log(LogType::Force, "Graphic pack error: {}", errorMsg); } // loads Cemu-style patches (patch_.asm) diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index f413c925..b24b1f2e 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -24,8 +24,6 @@ #include "Cafe/HW/Latte/Common/ShaderSerializer.h" #include "util/helpers/Serializer.h" -#include - #if BOOST_OS_WINDOWS #include #endif @@ -66,8 +64,6 @@ void LatteShaderCache_loadVulkanPipelineCache(uint64 cacheTitleId); bool LatteShaderCache_updatePipelineLoadingProgress(); void LatteShaderCache_ShowProgress(const std::function & loadUpdateFunc, bool isPipelines); -void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path pathGenericPre1_25_0, fs::path pathGenericPre1_16_0); - struct { struct @@ -245,10 +241,7 @@ void LatteShaderCache_load() RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId); // get cache file name const auto pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId); - const auto pathGenericPre1_25_0 = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}.bin", cacheTitleId); // before 1.25.0 - const auto pathGenericPre1_16_0 = ActiveSettings::GetCachePath("shaderCache/transferable/{:08x}.bin", CafeSystem::GetRPXHashBase()); // before 1.16.0 - LatteShaderCache_handleDeprecatedCacheFiles(pathGeneric, pathGenericPre1_25_0, pathGenericPre1_16_0); // calculate extraVersion for transferable and precompiled shader cache uint32 transferableExtraVersion = SHADER_CACHE_GENERIC_EXTRA_VERSION; fc_shaderCacheGeneric = FileCache::Open(pathGeneric.generic_wstring(), false, transferableExtraVersion); // legacy extra version (1.25.0 - 1.25.1b) @@ -758,31 +751,3 @@ bool LatteShaderCache_readSeparableShader(uint8* shaderInfoData, sint32 shaderIn return LatteShaderCache_readSeparablePixelShader(streamReader, version); return false; } - -#include - -void LatteShaderCache_handleDeprecatedCacheFiles(fs::path pathGeneric, fs::path pathGenericPre1_25_0, fs::path pathGenericPre1_16_0) -{ - std::error_code ec; - - bool hasOldCacheFiles = fs::exists(pathGenericPre1_25_0, ec) || fs::exists(pathGenericPre1_16_0, ec); - bool hasNewCacheFiles = fs::exists(pathGeneric, ec); - - if (hasOldCacheFiles && !hasNewCacheFiles) - { - // ask user if they want to delete or keep the old cache file - const auto infoMsg = L"Outdated shader cache\n\nCemu detected that the shader cache for this game is outdated\nOnly shader caches generated with Cemu 1.25.0 or above are supported\n\n" - "We recommend deleting the outdated cache file as it will no longer be used by Cemu"; - - wxMessageDialog dialog(nullptr, _(infoMsg), _("Outdated shader cache"), - wxYES_NO | wxCENTRE | wxICON_EXCLAMATION); - - dialog.SetYesNoLabels(_("Delete outdated cache file [recommended]"), _("Keep outdated cache file")); - const auto result = dialog.ShowModal(); - if (result == wxID_YES) - { - fs::remove(pathGenericPre1_16_0, ec); - fs::remove(pathGenericPre1_25_0, ec); - } - } -} diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 95386284..f7119b75 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -28,8 +28,6 @@ #include -#include - #ifndef VK_API_VERSION_MAJOR #define VK_API_VERSION_MAJOR(version) (((uint32_t)(version) >> 22) & 0x7FU) #define VK_API_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3FFU) @@ -281,12 +279,8 @@ void VulkanRenderer::GetDeviceFeatures() cemuLog_log(LogType::Force, "Shader round mode control not available on this device or driver. Some rendering issues might occur."); if (!m_featureControl.deviceExtensions.pipeline_creation_cache_control) - { cemuLog_log(LogType::Force, "VK_EXT_pipeline_creation_cache_control not supported. Cannot use asynchronous shader and pipeline compilation"); - // if async shader compilation is enabled show warning message - if (GetConfig().async_compile) - wxMessageBox(_("The currently installed graphics driver does not support the Vulkan extension necessary for asynchronous shader compilation. Asynchronous compilation cannot be used.\n \nRequired extension: VK_EXT_pipeline_creation_cache_control\n\nInstalling the latest graphics driver may solve this error."), _("Information"), wxOK | wxCENTRE); - } + if (!m_featureControl.deviceExtensions.custom_border_color_without_format) { if (m_featureControl.deviceExtensions.custom_border_color) diff --git a/src/Cafe/HW/MMU/MMU.cpp b/src/Cafe/HW/MMU/MMU.cpp index 1a776d6b..97a8e952 100644 --- a/src/Cafe/HW/MMU/MMU.cpp +++ b/src/Cafe/HW/MMU/MMU.cpp @@ -1,7 +1,6 @@ #include "Cafe/HW/MMU/MMU.h" #include "Cafe/GraphicPack/GraphicPack2.h" #include "util/MemMapper/MemMapper.h" -#include #include "config/ActiveSettings.h" uint8* memory_base = NULL; // base address of the reserved 4GB space @@ -90,8 +89,7 @@ void MMURange::mapMem() cemu_assert_debug(!m_isMapped); if (MemMapper::AllocateMemory(memory_base + baseAddress, size, MemMapper::PAGE_PERMISSION::P_RW, true) == nullptr) { - std::string errorMsg = fmt::format("Unable to allocate {} memory", name); - wxMessageBox(errorMsg.c_str(), "Error", wxOK | wxCENTRE | wxICON_ERROR); + cemuLog_log(LogType::Force, "Unable to allocate {} memory", name); #if BOOST_OS_WINDOWS ExitProcess(-1); #else @@ -131,9 +129,8 @@ void memory_init() memory_base = (uint8*)MemMapper::ReserveMemory(nullptr, (size_t)0x100000000, MemMapper::PAGE_PERMISSION::P_RW); if( !memory_base ) { - debug_printf("memory_init(): Unable to reserve 4GB of memory\n"); debugBreakpoint(); - wxMessageBox("Unable to reserve 4GB of memory\n", "Error", wxOK | wxCENTRE | wxICON_ERROR); + cemuLog_log(LogType::Force, "Unable to reserve 4GB of memory"); exit(-1); } for (auto& itr : g_mmuRanges) diff --git a/src/Cafe/OS/libs/nn_save/nn_save.cpp b/src/Cafe/OS/libs/nn_save/nn_save.cpp index 51f4c369..6e740f12 100644 --- a/src/Cafe/OS/libs/nn_save/nn_save.cpp +++ b/src/Cafe/OS/libs/nn_save/nn_save.cpp @@ -1,5 +1,4 @@ #include "Cafe/OS/common/OSCommon.h" -#include "gui/wxgui.h" #include "nn_save.h" #include "Cafe/OS/libs/nn_acp/nn_acp.h" @@ -203,79 +202,6 @@ namespace save return ConvertACPToSaveStatus(status); } - void _CheckAndMoveLegacySaves() - { - const uint64 titleId = CafeSystem::GetForegroundTitleId(); - - fs::path targetPath, sourcePath; - try - { - bool copiedUser = false, copiedCommon = false; - - const auto sourceSavePath = ActiveSettings::GetMlcPath("emulatorSave/{:08x}", CafeSystem::GetRPXHashBase()); - sourcePath = sourceSavePath; - - if (fs::exists(sourceSavePath) && is_directory(sourceSavePath)) - { - targetPath = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/{:08x}", GetTitleIdHigh(titleId), GetTitleIdLow(titleId), 0x80000001); - fs::create_directories(targetPath); - copy(sourceSavePath, targetPath, fs::copy_options::overwrite_existing | fs::copy_options::recursive); - copiedUser = true; - } - - const auto sourceCommonPath = ActiveSettings::GetMlcPath("emulatorSave/{:08x}_255", CafeSystem::GetRPXHashBase()); - sourcePath = sourceCommonPath; - - if (fs::exists(sourceCommonPath) && is_directory(sourceCommonPath)) - { - targetPath = ActiveSettings::GetMlcPath("usr/save/{:08x}/{:08x}/user/common", GetTitleIdHigh(titleId), GetTitleIdLow(titleId)); - fs::create_directories(targetPath); - copy(sourceCommonPath, targetPath, fs::copy_options::overwrite_existing | fs::copy_options::recursive); - copiedCommon = true; - } - - if (copiedUser) - fs::remove_all(sourceSavePath); - - if (copiedCommon) - fs::remove_all(sourceCommonPath); - } - catch (const std::exception& ex) - { -#if BOOST_OS_WINDOWS - std::wstringstream errorMsg; - errorMsg << L"Couldn't move your save files!" << std::endl << std::endl; - errorMsg << L"Error: " << ex.what() << std::endl << std::endl; - errorMsg << L"From:" << std::endl << sourcePath << std::endl << std::endl << "To:" << std::endl << targetPath; - - const DWORD lastError = GetLastError(); - if (lastError != ERROR_SUCCESS) - { - LPTSTR lpMsgBuf = nullptr; - FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, lastError, 0, (LPTSTR)&lpMsgBuf, 0, nullptr); - if (lpMsgBuf) - { - errorMsg << std::endl << std::endl << L"Details: " << lpMsgBuf; - LocalFree(lpMsgBuf); - } - else - { - errorMsg << std::endl << std::endl << L"Error Code: 0x" << std::hex << lastError; - } - } - - errorMsg << std::endl << std::endl << "Continuing will create a new save at the target location." << std::endl << "Do you want to continue?"; - - int result = wxMessageBox(errorMsg.str(), "Save Migration - Error", wxCENTRE | wxYES_NO | wxICON_ERROR); - if (result != wxYES) - { - exit(0); - return; - } -#endif - } - } - SAVEStatus SAVEInit() { const uint64 titleId = CafeSystem::GetForegroundTitleId(); @@ -295,8 +221,6 @@ namespace save SAVEMountSaveDir(); g_nn_save->initialized = true; - _CheckAndMoveLegacySaves(); - uint32 high = GetTitleIdHigh(titleId) & (~0xC); uint32 low = GetTitleIdLow(titleId); diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index 958ba459..ca27b1b8 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -6,7 +6,6 @@ #include "Cafe/OS/libs/coreinit/coreinit.h" #include "boost/program_options.hpp" -#include #include "config/ActiveSettings.h" #include "config/NetworkSettings.h" @@ -196,10 +195,7 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) } catch (const std::exception& ex) { - std::string errorMsg; - errorMsg.append("Error while trying to parse command line parameter:\n"); - errorMsg.append(ex.what()); - wxMessageBox(errorMsg, wxT("Parameter error"), wxICON_ERROR); + cemuLog_log(LogType::Force, "Error while trying to parse command line parameter: {}", ex.what()); return false; } From 8b265a9843692511f1cc4f80c26f9a6b5f31907f Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 7 Jun 2023 09:15:46 +0300 Subject: [PATCH 02/30] Move logic for getting button name for keyboard keys to guiWrapper --- src/gui/guiWrapper.cpp | 35 +++++++++++++++++-- src/gui/guiWrapper.h | 5 ++- src/input/api/Keyboard/KeyboardController.cpp | 33 +---------------- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index 1a5e999b..7cf082a5 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -195,12 +195,41 @@ bool gui_isPadWindowOpen() return g_window_info.pad_open; } -#if BOOST_OS_LINUX -std::string gui_gtkRawKeyCodeToString(uint32 keyCode) +std::string gui_RawKeyCodeToString(uint32 keyCode) { +#if BOOST_OS_WINDOWS + LONG scan_code = MapVirtualKeyA((UINT)keyCode, MAPVK_VK_TO_VSC_EX); + if(HIBYTE(scan_code)) + scan_code |= 0x100; + + // because MapVirtualKey strips the extended bit for some keys + switch (keyCode) + { + case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys + case VK_PRIOR: case VK_NEXT: // page up and page down + case VK_END: case VK_HOME: + case VK_INSERT: case VK_DELETE: + case VK_DIVIDE: // numpad slash + case VK_NUMLOCK: + { + scan_code |= 0x100; // set extended bit + break; + } + } + + scan_code <<= 16; + + char key_name[128]; + if (GetKeyNameTextA(scan_code, key_name, std::size(key_name)) != 0) + return key_name; + else + return fmt::format("key_{}", keyCode); +#elif BOOST_OS_LINUX return gdk_keyval_name(keyCode); -} +#else + return fmt::format("key_{}", keyCode); #endif +} void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, class wxWindow* wxw) { diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index 0e13596b..ebb29d83 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -140,9 +140,8 @@ bool gui_isFullScreen(); void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, class wxWindow* wxw); -#if BOOST_OS_LINUX -std::string gui_gtkRawKeyCodeToString(uint32 keyCode); -#endif +std::string gui_RawKeyCodeToString(uint32 keyCode); + /* * Returns true if a screenshot request is queued * Once this function has returned true, it will reset back to diff --git a/src/input/api/Keyboard/KeyboardController.cpp b/src/input/api/Keyboard/KeyboardController.cpp index 1f5458fa..7dc1646d 100644 --- a/src/input/api/Keyboard/KeyboardController.cpp +++ b/src/input/api/Keyboard/KeyboardController.cpp @@ -11,38 +11,7 @@ KeyboardController::KeyboardController() std::string KeyboardController::get_button_name(uint64 button) const { -#if BOOST_OS_WINDOWS - LONG scan_code = MapVirtualKeyA((UINT)button, MAPVK_VK_TO_VSC_EX); - if(HIBYTE(scan_code)) - scan_code |= 0x100; - - // because MapVirtualKey strips the extended bit for some keys - switch (button) - { - case VK_LEFT: case VK_UP: case VK_RIGHT: case VK_DOWN: // arrow keys - case VK_PRIOR: case VK_NEXT: // page up and page down - case VK_END: case VK_HOME: - case VK_INSERT: case VK_DELETE: - case VK_DIVIDE: // numpad slash - case VK_NUMLOCK: - { - scan_code |= 0x100; // set extended bit - break; - } - } - - scan_code <<= 16; - - char key_name[128]; - if (GetKeyNameTextA(scan_code, key_name, std::size(key_name)) != 0) - return key_name; - else - return fmt::format("key_{}", button); -#elif BOOST_OS_LINUX - return gui_gtkRawKeyCodeToString(button); -#else - return fmt::format("key_{}", button); -#endif + return gui_RawKeyCodeToString(button); } extern WindowInfo g_window_info; From 466f03febcecbd6ae109bd70cca63b560db7f4cb Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 7 Jun 2023 10:23:13 +0300 Subject: [PATCH 03/30] Move screenshot logic with wxWidgets to guiWrapper --- src/Cafe/CMakeLists.txt | 4 --- src/Cafe/HW/Latte/Renderer/Renderer.cpp | 43 ++----------------------- src/gui/guiWrapper.cpp | 32 ++++++++++++++++++ src/gui/guiWrapper.h | 2 ++ 4 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index 8da22176..4b9ec166 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -508,10 +508,6 @@ if (ENABLE_WAYLAND) target_link_libraries(CemuCafe PUBLIC Wayland::Client) endif() -if (ENABLE_WXWIDGETS) - target_link_libraries(CemuCafe PRIVATE wx::base wx::core) -endif() - if(WIN32) target_link_libraries(CemuCafe PRIVATE iphlpapi) endif() diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.cpp b/src/Cafe/HW/Latte/Renderer/Renderer.cpp index 63ba5a7d..b8585c77 100644 --- a/src/Cafe/HW/Latte/Renderer/Renderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Renderer.cpp @@ -10,10 +10,6 @@ #include "config/ActiveSettings.h" -#include -#include -#include -#include std::unique_ptr g_renderer; @@ -143,38 +139,6 @@ static std::optional GenerateScreenshotFilename(bool isDRC) return std::nullopt; } -std::mutex s_clipboardMutex; - -static bool SaveScreenshotToClipboard(const wxImage &image) -{ - bool success = false; - - s_clipboardMutex.lock(); - if (wxTheClipboard->Open()) - { - wxTheClipboard->SetData(new wxImageDataObject(image)); - wxTheClipboard->Close(); - success = true; - } - s_clipboardMutex.unlock(); - - return success; -} - -static bool SaveScreenshotToFile(const wxImage &image, bool mainWindow) -{ - auto path = GenerateScreenshotFilename(!mainWindow); - if (!path) return false; - - std::error_code ec; - fs::create_directories(path->parent_path(), ec); - if (ec) return false; - - // suspend wxWidgets logging for the lifetime this object, to prevent a message box if wxImage::SaveFile fails - wxLogNull _logNo; - return image.SaveFile(path->wstring()); -} - static void ScreenshotThread(std::vector data, bool save_screenshot, int width, int height, bool mainWindow) { #if BOOST_OS_WINDOWS @@ -182,12 +146,10 @@ static void ScreenshotThread(std::vector data, bool save_screenshot, int // to make this work we need to call OleInitialize() on the same thread OleInitialize(nullptr); #endif - - wxImage image(width, height, data.data(), true); if (mainWindow) { - if(SaveScreenshotToClipboard(image)) + if(gui_saveScreenshotToClipboard(data, width, height)) { if (!save_screenshot) LatteOverlay_pushNotification("Screenshot saved to clipboard", 2500); @@ -200,7 +162,8 @@ static void ScreenshotThread(std::vector data, bool save_screenshot, int if (save_screenshot) { - if (SaveScreenshotToFile(image, mainWindow)) + auto imagePath = GenerateScreenshotFilename(mainWindow); + if (imagePath.has_value() && gui_saveScreenshotToFile(imagePath.value(), data, width, height)) { if (mainWindow) LatteOverlay_pushNotification("Screenshot saved", 2500); diff --git a/src/gui/guiWrapper.cpp b/src/gui/guiWrapper.cpp index 7cf082a5..1ca86284 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/guiWrapper.cpp @@ -21,6 +21,7 @@ #include "Cafe/CafeSystem.h" #include "wxHelper.h" +#include WindowInfo g_window_info {}; @@ -231,6 +232,37 @@ std::string gui_RawKeyCodeToString(uint32 keyCode) #endif } +bool gui_saveScreenshotToFile(const fs::path& imagePath, std::vector& data, int width, int height) +{ + + std::error_code ec; + fs::create_directories(imagePath.parent_path(), ec); + if (ec) return false; + + // suspend wxWidgets logging for the lifetime this object, to prevent a message box if wxImage::SaveFile fails + wxLogNull _logNo; + wxImage image(width, height, data.data(), true); + return image.SaveFile(imagePath.wstring()); +} + +bool gui_saveScreenshotToClipboard(std::vector& data, int width, int height) +{ + static std::mutex s_clipboardMutex; + bool success = false; + + s_clipboardMutex.lock(); + if (wxTheClipboard->Open()) + { + wxImage image(width, height, data.data(), true); + wxTheClipboard->SetData(new wxImageDataObject(image)); + wxTheClipboard->Close(); + success = true; + } + s_clipboardMutex.unlock(); + + return success; +} + void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, class wxWindow* wxw) { #if BOOST_OS_WINDOWS diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index ebb29d83..396a4823 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -142,6 +142,8 @@ void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, c std::string gui_RawKeyCodeToString(uint32 keyCode); +bool gui_saveScreenshotToFile(const fs::path& imagePath, std::vector& data, int width, int height); +bool gui_saveScreenshotToClipboard(std::vector& data, int width, int height); /* * Returns true if a screenshot request is queued * Once this function has returned true, it will reset back to From c0147f0c2e26e33657d3caec940bf5941edcb669 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 7 Jun 2023 10:24:46 +0300 Subject: [PATCH 04/30] Move audioDebugger to ui --- src/audio/CMakeLists.txt | 11 ----------- src/gui/CMakeLists.txt | 2 ++ src/gui/MainWindow.cpp | 2 +- .../debugger/AudioDebuggerWindow.cpp} | 2 +- .../debugger/AudioDebuggerWindow.h} | 0 5 files changed, 4 insertions(+), 13 deletions(-) rename src/{audio/audioDebuggerWindow.cpp => gui/debugger/AudioDebuggerWindow.cpp} (99%) rename src/{audio/audioDebuggerWindow.h => gui/debugger/AudioDebuggerWindow.h} (100%) diff --git a/src/audio/CMakeLists.txt b/src/audio/CMakeLists.txt index a240477b..b78de255 100644 --- a/src/audio/CMakeLists.txt +++ b/src/audio/CMakeLists.txt @@ -7,12 +7,6 @@ add_library(CemuAudio set_property(TARGET CemuAudio PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -# move these to UI folder -target_sources(CemuAudio PRIVATE - audioDebuggerWindow.cpp - audioDebuggerWindow.h -) - if(WIN32) target_sources(CemuAudio PRIVATE DirectSoundAPI.cpp @@ -48,8 +42,3 @@ if(ENABLE_CUBEB) # PUBLIC because cubeb.h/cubeb.h is included in CubebAPI.h target_link_libraries(CemuAudio PUBLIC cubeb::cubeb) endif() - -if (ENABLE_WXWIDGETS) - # PUBLIC because wx/wx.h is included in audioDebuggerWindow.h - target_link_libraries(CemuAudio PUBLIC wx::base wx::core) -endif() diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 90dc91c0..a1572c13 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -22,6 +22,8 @@ add_library(CemuGui components/wxLogCtrl.h components/wxTitleManagerList.cpp components/wxTitleManagerList.h + debugger/AudioDebuggerWindow.cpp + debugger/AudioDebuggerWindow.h debugger/BreakpointWindow.cpp debugger/BreakpointWindow.h debugger/DebuggerWindow2.cpp diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 7fe4ec54..852e32a7 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -9,7 +9,6 @@ #include "gui/PadViewFrame.h" #include "gui/windows/TextureRelationViewer/TextureRelationWindow.h" #include "gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h" -#include "audio/audioDebuggerWindow.h" #include "gui/canvas/OpenGLCanvas.h" #include "gui/canvas/VulkanCanvas.h" #include "Cafe/OS/libs/nn_nfp/nn_nfp.h" @@ -17,6 +16,7 @@ #include "Cafe/IOSU/legacy/iosu_crypto.h" #include "Cafe/GameProfile/GameProfile.h" #include "gui/debugger/DebuggerWindow2.h" +#include "gui/debugger/AudioDebuggerWindow.h" #include "util/helpers/helpers.h" #include "config/CemuConfig.h" #include "Cemu/DiscordPresence/DiscordPresence.h" diff --git a/src/audio/audioDebuggerWindow.cpp b/src/gui/debugger/AudioDebuggerWindow.cpp similarity index 99% rename from src/audio/audioDebuggerWindow.cpp rename to src/gui/debugger/AudioDebuggerWindow.cpp index 41107820..d082630e 100644 --- a/src/audio/audioDebuggerWindow.cpp +++ b/src/gui/debugger/AudioDebuggerWindow.cpp @@ -1,5 +1,5 @@ #include "gui/wxgui.h" -#include "audioDebuggerWindow.h" +#include "gui/debugger/AudioDebuggerWindow.h" #include "Cafe/OS/libs/snd_core/ax.h" #include "Cafe/OS/libs/snd_core/ax_internal.h" diff --git a/src/audio/audioDebuggerWindow.h b/src/gui/debugger/AudioDebuggerWindow.h similarity index 100% rename from src/audio/audioDebuggerWindow.h rename to src/gui/debugger/AudioDebuggerWindow.h From e315a7ecb004b11c8627a86b5b10d940cd60f81d Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 7 Jun 2023 10:25:30 +0300 Subject: [PATCH 05/30] Remove wxWidgets dependencies --- src/Cafe/HW/Espresso/Debugger/Debugger.cpp | 2 -- src/Cafe/HW/Latte/Renderer/OpenGL/GLCanvas.h | 5 +++ .../Latte/Renderer/OpenGL/OpenGLRenderer.cpp | 2 +- .../HW/Latte/Renderer/Vulkan/VsyncDriver.cpp | 2 -- src/Cafe/IOSU/legacy/iosu_act.cpp | 1 - src/Cafe/OS/libs/erreula/erreula.cpp | 2 -- src/Cafe/OS/libs/padscore/padscore.cpp | 2 +- src/Cafe/OS/libs/vpad/vpad.cpp | 2 +- src/Cemu/Logging/CemuLogging.cpp | 6 ++-- .../Tools/DownloadManager/DownloadManager.cpp | 18 +++++----- src/config/CMakeLists.txt | 6 ---- src/config/CemuConfig.cpp | 4 +-- src/config/CemuConfig.h | 4 +-- src/gui/guiWrapper.h | 36 ++++++------------- src/input/CMakeLists.txt | 4 --- src/input/emulated/ClassicController.cpp | 28 +++++++-------- src/input/emulated/ProController.cpp | 32 ++++++++--------- src/input/emulated/VPADController.cpp | 31 ++++++++-------- src/input/emulated/WPADController.h | 2 -- src/input/emulated/WiimoteController.cpp | 19 +++++----- src/main.cpp | 4 --- src/mainLLE.cpp | 2 -- src/util/CMakeLists.txt | 4 --- src/util/helpers/helpers.cpp | 2 -- src/util/math/quaternion.h | 1 - 25 files changed, 84 insertions(+), 137 deletions(-) create mode 100644 src/Cafe/HW/Latte/Renderer/OpenGL/GLCanvas.h diff --git a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp index b6417080..f7361405 100644 --- a/src/Cafe/HW/Espresso/Debugger/Debugger.cpp +++ b/src/Cafe/HW/Espresso/Debugger/Debugger.cpp @@ -4,8 +4,6 @@ #include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h" #include "Cemu/ExpressionParser/ExpressionParser.h" -#include "gui/debugger/DebuggerWindow2.h" - #include "Cafe/OS/libs/coreinit/coreinit.h" #if BOOST_OS_WINDOWS diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/GLCanvas.h b/src/Cafe/HW/Latte/Renderer/OpenGL/GLCanvas.h new file mode 100644 index 00000000..cb4665a5 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/GLCanvas.h @@ -0,0 +1,5 @@ +#pragma once + +bool GLCanvas_MakeCurrent(bool padView); +void GLCanvas_SwapBuffers(bool swapTV, bool swapDRC); +bool GLCanvas_HasPadViewOpen(); diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp index 4dfdc52b..7a92f9f9 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp @@ -19,7 +19,7 @@ #include "Cafe/HW/Latte/ISA/RegDefines.h" #include "Cafe/OS/libs/gx2/GX2.h" -#include "gui/canvas/OpenGLCanvas.h" +#include "GLCanvas.h" #define STRINGIFY2(X) #X #define STRINGIFY(X) STRINGIFY2(X) diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.cpp index 6ccca80f..cbbed5fb 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.cpp @@ -1,5 +1,3 @@ -#include "gui/MainWindow.h" - #if BOOST_OS_WINDOWS #include diff --git a/src/Cafe/IOSU/legacy/iosu_act.cpp b/src/Cafe/IOSU/legacy/iosu_act.cpp index c830fc4f..d6e592ed 100644 --- a/src/Cafe/IOSU/legacy/iosu_act.cpp +++ b/src/Cafe/IOSU/legacy/iosu_act.cpp @@ -2,7 +2,6 @@ #include "iosu_ioctl.h" #include "Cafe/OS/libs/nn_common.h" -#include "gui/CemuApp.h" #include #include diff --git a/src/Cafe/OS/libs/erreula/erreula.cpp b/src/Cafe/OS/libs/erreula/erreula.cpp index e8b82c2f..751294c0 100644 --- a/src/Cafe/OS/libs/erreula/erreula.cpp +++ b/src/Cafe/OS/libs/erreula/erreula.cpp @@ -6,8 +6,6 @@ #include #include "imgui/imgui_extension.h" -#include - #include "Cafe/OS/libs/coreinit/coreinit_FS.h" #include "Cafe/OS/libs/vpad/vpad.h" diff --git a/src/Cafe/OS/libs/padscore/padscore.cpp b/src/Cafe/OS/libs/padscore/padscore.cpp index 5c1203bf..1d447343 100644 --- a/src/Cafe/OS/libs/padscore/padscore.cpp +++ b/src/Cafe/OS/libs/padscore/padscore.cpp @@ -1,6 +1,6 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/HW/Espresso/PPCCallback.h" -#include "gui/wxgui.h" +#include "gui/guiWrapper.h" #include "Cafe/OS/libs/padscore/padscore.h" #include "Cafe/OS/libs/coreinit/coreinit_Time.h" #include "Cafe/OS/libs/coreinit/coreinit_Alarm.h" diff --git a/src/Cafe/OS/libs/vpad/vpad.cpp b/src/Cafe/OS/libs/vpad/vpad.cpp index d869eef1..2ac47c96 100644 --- a/src/Cafe/OS/libs/vpad/vpad.cpp +++ b/src/Cafe/OS/libs/vpad/vpad.cpp @@ -1,6 +1,6 @@ #include "Cafe/OS/common/OSCommon.h" #include "Cafe/HW/Espresso/PPCCallback.h" -#include "gui/wxgui.h" +#include "gui/guiWrapper.h" #include "Cafe/OS/libs/vpad/vpad.h" #include "audio/IAudioAPI.h" #include "Cafe/OS/libs/coreinit/coreinit_Time.h" diff --git a/src/Cemu/Logging/CemuLogging.cpp b/src/Cemu/Logging/CemuLogging.cpp index ed8441a7..8203abd7 100644 --- a/src/Cemu/Logging/CemuLogging.cpp +++ b/src/Cemu/Logging/CemuLogging.cpp @@ -1,6 +1,6 @@ #include "CemuLogging.h" #include "config/CemuConfig.h" -#include "gui/LoggingWindow.h" +#include "gui/guiWrapper.h" #include "config/ActiveSettings.h" #include "util/helpers/helpers.h" @@ -158,9 +158,9 @@ bool cemuLog_log(LogType type, std::string_view text) const auto it = std::find_if(g_logging_window_mapping.cbegin(), g_logging_window_mapping.cend(), [type](const auto& entry) { return entry.first == type; }); if (it == g_logging_window_mapping.cend()) - LoggingWindow::Log(text); + gui_loggingWindowLog(text); else - LoggingWindow::Log(it->second, text); + gui_loggingWindowLog(it->second, text); return true; } diff --git a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp index bb8eaa92..e355936b 100644 --- a/src/Cemu/Tools/DownloadManager/DownloadManager.cpp +++ b/src/Cemu/Tools/DownloadManager/DownloadManager.cpp @@ -1,7 +1,7 @@ #include "Cemu/Tools/DownloadManager/DownloadManager.h" #include "Cafe/Account/Account.h" -#include "gui/CemuApp.h" +#include "gui/guiWrapper.h" #include "util/crypto/md5.h" #include "Cafe/TitleList/TitleId.h" #include "Common/FileStream.h" @@ -10,7 +10,6 @@ #include "config/ActiveSettings.h" #include "util/ThreadPool/ThreadPool.h" #include "util/helpers/enum_array.hpp" -#include "gui/MainWindow.h" #include "Cafe/Filesystem/FST/FST.h" #include "Cafe/TitleList/TitleList.h" @@ -20,15 +19,16 @@ #include #include -#include "gui/helpers/wxHelpers.h" - #include "Cemu/napi/napi.h" #include "util/helpers/Serializer.h" FileCache* s_nupFileCache = nullptr; -/* version list */ +// Todo: replace this +std::string _(const std::string& str) { return str; } +std::string from_wxString(const std::string& str){ return str; } +/* version list */ void DownloadManager::downloadTitleVersionList() { if (m_hasTitleVersionList) @@ -371,7 +371,7 @@ bool DownloadManager::syncAccountTickets() for (auto& tiv : resultTicketIds.tivs) { index++; - std::string msg = _("Downloading account ticket").ToStdString(); + std::string msg = _("Downloading account ticket"); msg.append(fmt::format(" {0}/{1}", index, count)); setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); // skip if already cached @@ -508,7 +508,7 @@ bool DownloadManager::syncUpdateTickets() if (titleIdParser.GetType() != TitleIdParser::TITLE_TYPE::BASE_TITLE_UPDATE) continue; - std::string msg = _("Downloading ticket").ToStdString(); + std::string msg = _("Downloading ticket"); msg.append(fmt::format(" {0}/{1}", updateIndex, numUpdates)); updateIndex++; setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); @@ -561,7 +561,7 @@ bool DownloadManager::syncTicketCache() for (auto& ticketInfo : m_ticketCache) { index++; - std::string msg = _("Downloading meta data").ToStdString(); + std::string msg = _("Downloading meta data"); msg.append(fmt::format(" {0}/{1}", index, count)); setStatusMessage(msg, DLMGR_STATUS_CODE::CONNECTING); prepareIDBE(ticketInfo.titleId); @@ -1449,7 +1449,7 @@ void DownloadManager::asyncPackageInstall(Package* package) reportPackageStatus(package); checkPackagesState(); // lastly request game list to be refreshed - MainWindow::RequestGameListRefresh(); + gui_requestGameListRefresh(); return; } diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt index f02b95d4..c531e288 100644 --- a/src/config/CMakeLists.txt +++ b/src/config/CMakeLists.txt @@ -27,9 +27,3 @@ target_link_libraries(CemuConfig PRIVATE Boost::program_options pugixml::pugixml ) - -if (ENABLE_WXWIDGETS) - # PUBLIC because wx/language.h is included in CemuConfig.h - # Could be made PRIVATE by using 0 instead of wxLANGUAGE_DEFAULT - target_link_libraries(CemuConfig PUBLIC wx::base wx::core) -endif() diff --git a/src/config/CemuConfig.cpp b/src/config/CemuConfig.cpp index 5d99a23d..8938da3e 100644 --- a/src/config/CemuConfig.cpp +++ b/src/config/CemuConfig.cpp @@ -3,8 +3,6 @@ #include "util/helpers/helpers.h" #include "config/ActiveSettings.h" -#include - #include "PermanentConfig.h" #include "ActiveSettings.h" @@ -50,7 +48,7 @@ void CemuConfig::Load(XMLConfigParser& parser) permanent_storage = parser.get("permanent_storage", permanent_storage); - language = parser.get("language", wxLANGUAGE_DEFAULT); + language = parser.get("language", 0); use_discord_presence = parser.get("use_discord_presence", true); fullscreen_menubar = parser.get("fullscreen_menubar", false); feral_gamemode = parser.get("feral_gamemode", false); diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index e17a9344..f3836a9c 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -5,8 +5,6 @@ #include "util/math/vector2.h" #include "Cafe/Account/Account.h" -#include - struct GameEntry { GameEntry() = default; @@ -361,7 +359,7 @@ struct CemuConfig ConfigValue permanent_storage{ true }; - ConfigValue language{ wxLANGUAGE_DEFAULT }; + ConfigValue language{ 0 }; ConfigValue use_discord_presence{ true }; ConfigValue mlc_path {}; ConfigValue fullscreen_menubar{ false }; diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index 396a4823..90d2ffae 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -2,15 +2,6 @@ #include -#if BOOST_OS_LINUX -#include "xcb/xproto.h" -#include -#endif - -#if BOOST_OS_MACOS -#include -#endif - struct WindowHandleInfo { #if BOOST_OS_WINDOWS @@ -39,23 +30,9 @@ struct WindowHandleInfo enum struct PlatformKeyCodes : uint32 { -#if BOOST_OS_WINDOWS - LCONTROL = VK_LCONTROL, - RCONTROL = VK_RCONTROL, - TAB = VK_TAB, -#elif BOOST_OS_LINUX - LCONTROL = GDK_KEY_Control_L, - RCONTROL = GDK_KEY_Control_R, - TAB = GDK_KEY_Tab, -#elif BOOST_OS_MACOS - LCONTROL = kVK_Control, - RCONTROL = kVK_RightControl, - TAB = kVK_Tab, -#else - LCONTROL = 0, - RCONTROL = 0, - TAB = 0, -#endif + LCONTROL, + RCONTROL, + TAB }; struct WindowInfo @@ -118,6 +95,11 @@ struct WindowInfo std::unordered_map m_keydown; }; +extern bool g_inputConfigWindowHasFocus; + +void gui_loggingWindowLog(std::string_view filter, std::string_view message); +void gui_loggingWindowLog(std::string_view message); + void gui_create(); WindowInfo& gui_getWindowInfo(); @@ -140,6 +122,8 @@ bool gui_isFullScreen(); void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, class wxWindow* wxw); +void gui_requestGameListRefresh(); + std::string gui_RawKeyCodeToString(uint32 keyCode); bool gui_saveScreenshotToFile(const fs::path& imagePath, std::vector& data, int width, int height); diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index ecb88cd2..8fc29044 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -87,7 +87,3 @@ target_link_libraries(CemuInput PRIVATE pugixml::pugixml SDL2::SDL2 ) - -if (ENABLE_WXWIDGETS) - target_link_libraries(CemuInput PRIVATE wx::base wx::core) -endif() diff --git a/src/input/emulated/ClassicController.cpp b/src/input/emulated/ClassicController.cpp index 5a3bdb43..e218cded 100644 --- a/src/input/emulated/ClassicController.cpp +++ b/src/input/emulated/ClassicController.cpp @@ -3,8 +3,6 @@ #include "input/api/Controller.h" #include "input/api/SDL/SDLController.h" -#include - ClassicController::ClassicController(size_t player_index) : WPADController(player_index, kDataFormat_CLASSIC) { @@ -70,22 +68,22 @@ std::string_view ClassicController::get_button_name(ButtonId id) case kButtonId_Plus: return "+"; case kButtonId_Minus: return "-"; - case kButtonId_Home: return wxTRANSLATE("home"); + case kButtonId_Home: return "home"; - case kButtonId_Up: return wxTRANSLATE("up"); - case kButtonId_Down: return wxTRANSLATE("down"); - case kButtonId_Left: return wxTRANSLATE("left"); - case kButtonId_Right: return wxTRANSLATE("right"); + case kButtonId_Up: return "up"; + case kButtonId_Down: return "down"; + case kButtonId_Left: return "left"; + case kButtonId_Right: return "right"; - case kButtonId_StickL_Up: return wxTRANSLATE("up"); - case kButtonId_StickL_Down: return wxTRANSLATE("down"); - case kButtonId_StickL_Left: return wxTRANSLATE("left"); - case kButtonId_StickL_Right: return wxTRANSLATE("right"); + case kButtonId_StickL_Up: return "up"; + case kButtonId_StickL_Down: return "down"; + case kButtonId_StickL_Left: return "left"; + case kButtonId_StickL_Right: return "right"; - case kButtonId_StickR_Up: return wxTRANSLATE("up"); - case kButtonId_StickR_Down: return wxTRANSLATE("down"); - case kButtonId_StickR_Left: return wxTRANSLATE("left"); - case kButtonId_StickR_Right: return wxTRANSLATE("right"); + case kButtonId_StickR_Up: return "up"; + case kButtonId_StickR_Down: return "down"; + case kButtonId_StickR_Left: return "left"; + case kButtonId_StickR_Right: return "right"; default: return ""; diff --git a/src/input/emulated/ProController.cpp b/src/input/emulated/ProController.cpp index a546cde8..521f88b2 100644 --- a/src/input/emulated/ProController.cpp +++ b/src/input/emulated/ProController.cpp @@ -3,8 +3,6 @@ #include "input/api/Controller.h" #include "input/api/SDL/SDLController.h" -#include - ProController::ProController(size_t player_index) : WPADController(player_index, kDataFormat_URCC) { @@ -75,21 +73,21 @@ std::string_view ProController::get_button_name(ButtonId id) case kButtonId_ZR: return "ZR"; case kButtonId_Plus: return "+"; case kButtonId_Minus: return "-"; - case kButtonId_Up: return wxTRANSLATE("up"); - case kButtonId_Down: return wxTRANSLATE("down"); - case kButtonId_Left: return wxTRANSLATE("left"); - case kButtonId_Right: return wxTRANSLATE("right"); - case kButtonId_StickL: return wxTRANSLATE("click"); - case kButtonId_StickR: return wxTRANSLATE("click"); - case kButtonId_StickL_Up: return wxTRANSLATE("up"); - case kButtonId_StickL_Down: return wxTRANSLATE("down"); - case kButtonId_StickL_Left: return wxTRANSLATE("left"); - case kButtonId_StickL_Right: return wxTRANSLATE("right"); - case kButtonId_StickR_Up: return wxTRANSLATE("up"); - case kButtonId_StickR_Down: return wxTRANSLATE("down"); - case kButtonId_StickR_Left: return wxTRANSLATE("left"); - case kButtonId_StickR_Right: return wxTRANSLATE("right"); - case kButtonId_Home: return wxTRANSLATE("home"); + case kButtonId_Up: return "up"; + case kButtonId_Down: return "down"; + case kButtonId_Left: return "left"; + case kButtonId_Right: return "right"; + case kButtonId_StickL: return "click"; + case kButtonId_StickR: return "click"; + case kButtonId_StickL_Up: return "up"; + case kButtonId_StickL_Down: return "down"; + case kButtonId_StickL_Left: return "left"; + case kButtonId_StickL_Right: return "right"; + case kButtonId_StickR_Up: return "up"; + case kButtonId_StickR_Down: return "down"; + case kButtonId_StickR_Left: return "left"; + case kButtonId_StickR_Right: return "right"; + case kButtonId_Home: return "home"; default: cemu_assert_debug(false); return ""; diff --git a/src/input/emulated/VPADController.cpp b/src/input/emulated/VPADController.cpp index aeb56395..eff221ef 100644 --- a/src/input/emulated/VPADController.cpp +++ b/src/input/emulated/VPADController.cpp @@ -5,7 +5,6 @@ #include "input/InputManager.h" #include "Cafe/HW/Latte/Core/Latte.h" #include "Cafe/CafeSystem.h" -#include enum ControllerVPADMapping2 : uint32 { @@ -367,21 +366,21 @@ std::string_view VPADController::get_button_name(ButtonId id) case kButtonId_ZR: return "ZR"; case kButtonId_Plus: return "+"; case kButtonId_Minus: return "-"; - case kButtonId_Up: return wxTRANSLATE("up"); - case kButtonId_Down: return wxTRANSLATE("down"); - case kButtonId_Left: return wxTRANSLATE("left"); - case kButtonId_Right: return wxTRANSLATE("right"); - case kButtonId_StickL: return wxTRANSLATE("click"); - case kButtonId_StickR: return wxTRANSLATE("click"); - case kButtonId_StickL_Up: return wxTRANSLATE("up"); - case kButtonId_StickL_Down: return wxTRANSLATE("down"); - case kButtonId_StickL_Left: return wxTRANSLATE("left"); - case kButtonId_StickL_Right: return wxTRANSLATE("right"); - case kButtonId_StickR_Up: return wxTRANSLATE("up"); - case kButtonId_StickR_Down: return wxTRANSLATE("down"); - case kButtonId_StickR_Left: return wxTRANSLATE("left"); - case kButtonId_StickR_Right: return wxTRANSLATE("right"); - case kButtonId_Home: return wxTRANSLATE("home"); + case kButtonId_Up: return "up"; + case kButtonId_Down: return "down"; + case kButtonId_Left: return "left"; + case kButtonId_Right: return "right"; + case kButtonId_StickL: return "click"; + case kButtonId_StickR: return "click"; + case kButtonId_StickL_Up: return "up"; + case kButtonId_StickL_Down: return "down"; + case kButtonId_StickL_Left: return "left"; + case kButtonId_StickL_Right: return "right"; + case kButtonId_StickR_Up: return "up"; + case kButtonId_StickR_Down: return "down"; + case kButtonId_StickR_Left: return "left"; + case kButtonId_StickR_Right: return "right"; + case kButtonId_Home: return "home"; default: cemu_assert_debug(false); return ""; diff --git a/src/input/emulated/WPADController.h b/src/input/emulated/WPADController.h index 2636b74a..39df5098 100644 --- a/src/input/emulated/WPADController.h +++ b/src/input/emulated/WPADController.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "input/emulated/EmulatedController.h" #include "Cafe/OS/libs/padscore/padscore.h" #include "Cafe/OS/libs/vpad/vpad.h" diff --git a/src/input/emulated/WiimoteController.cpp b/src/input/emulated/WiimoteController.cpp index b090088e..578a9527 100644 --- a/src/input/emulated/WiimoteController.cpp +++ b/src/input/emulated/WiimoteController.cpp @@ -2,7 +2,6 @@ #include "input/api/Controller.h" #include "input/api/Wiimote/NativeWiimoteController.h" -#include WiimoteController::WiimoteController(size_t player_index) : WPADController(player_index, kDataFormat_CORE_ACC_DPD) @@ -159,22 +158,22 @@ std::string_view WiimoteController::get_button_name(ButtonId id) case kButtonId_1: return "1"; case kButtonId_2: return "2"; - case kButtonId_Home: return wxTRANSLATE("home"); + case kButtonId_Home: return "home"; case kButtonId_Plus: return "+"; case kButtonId_Minus: return "-"; - case kButtonId_Up: return wxTRANSLATE("up"); - case kButtonId_Down: return wxTRANSLATE("down"); - case kButtonId_Left: return wxTRANSLATE("left"); - case kButtonId_Right: return wxTRANSLATE("right"); + case kButtonId_Up: return "up"; + case kButtonId_Down: return "down"; + case kButtonId_Left: return "left"; + case kButtonId_Right: return "right"; case kButtonId_Nunchuck_Z: return "Z"; case kButtonId_Nunchuck_C: return "C"; - case kButtonId_Nunchuck_Up: return wxTRANSLATE("up"); - case kButtonId_Nunchuck_Down: return wxTRANSLATE("down"); - case kButtonId_Nunchuck_Left: return wxTRANSLATE("left"); - case kButtonId_Nunchuck_Right: return wxTRANSLATE("right"); + case kButtonId_Nunchuck_Up: return "up"; + case kButtonId_Nunchuck_Down: return "down"; + case kButtonId_Nunchuck_Left: return "left"; + case kButtonId_Nunchuck_Right: return "right"; default: return ""; diff --git a/src/main.cpp b/src/main.cpp index 2d281d88..aba8c811 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,5 @@ #include "gui/guiWrapper.h" -#include "gui/wxgui.h" #include "util/crypto/aes128.h" -#include "gui/MainWindow.h" #include "Cafe/OS/RPL/rpl.h" #include "Cafe/OS/RPL/rpl_symbol_storage.h" #include "Cafe/OS/libs/gx2/GX2.h" @@ -9,7 +7,6 @@ #include "Cafe/GraphicPack/GraphicPack2.h" #include "config/CemuConfig.h" #include "config/NetworkSettings.h" -#include "gui/CemuApp.h" #include "Cafe/HW/Latte/Core/LatteOverlay.h" #include "config/LaunchSettings.h" #include "Cafe/OS/libs/coreinit/coreinit_Thread.h" @@ -21,7 +18,6 @@ #include "Common/ExceptionHandler/ExceptionHandler.h" #include "Common/cpu_features.h" -#include #include "util/helpers/helpers.h" #include "config/ActiveSettings.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h" diff --git a/src/mainLLE.cpp b/src/mainLLE.cpp index 2b7c18a0..ac100ccc 100644 --- a/src/mainLLE.cpp +++ b/src/mainLLE.cpp @@ -1,6 +1,4 @@ -#include "gui/wxgui.h" #include "util/crypto/aes128.h" -#include "gui/MainWindow.h" #include "gui/guiWrapper.h" #include "Common/FileStream.h" diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 5af88176..f1bb09a6 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -97,7 +97,3 @@ target_link_libraries(CemuUtil PRIVATE Boost::nowide OpenSSL::Crypto ) - -if (ENABLE_WXWIDGETS) - target_link_libraries(CemuUtil PRIVATE wx::base wx::core) -endif() diff --git a/src/util/helpers/helpers.cpp b/src/util/helpers/helpers.cpp index b556db36..986933d5 100644 --- a/src/util/helpers/helpers.cpp +++ b/src/util/helpers/helpers.cpp @@ -5,8 +5,6 @@ #include #include -#include - #include "config/ActiveSettings.h" #include diff --git a/src/util/math/quaternion.h b/src/util/math/quaternion.h index 764cc44c..3c40d1ea 100644 --- a/src/util/math/quaternion.h +++ b/src/util/math/quaternion.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include "util/math/vector3.h" #define DEG2RAD(__d__) ((__d__ * M_PI) / 180.0f ) #define RAD2DEG(__r__) ((__r__ * 180.0f) / M_PI) From be77df52b921156a610daefdcfd1ac61560ea99d Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Wed, 7 Jun 2023 11:34:03 +0300 Subject: [PATCH 06/30] Move wxWidgets gui to wxGui --- src/gui/CMakeLists.txt | 176 +----------------- src/gui/canvas/OpenGLCanvas.h | 8 - src/gui/wxGui/CMakeLists.txt | 171 +++++++++++++++++ src/gui/{ => wxGui}/CemuApp.cpp | 14 +- src/gui/{ => wxGui}/CemuApp.h | 0 src/gui/{ => wxGui}/CemuUpdateWindow.cpp | 2 +- src/gui/{ => wxGui}/CemuUpdateWindow.h | 0 src/gui/{ => wxGui}/ChecksumTool.cpp | 8 +- src/gui/{ => wxGui}/ChecksumTool.h | 2 +- .../DownloadGraphicPacksWindow.cpp | 4 +- .../{ => wxGui}/DownloadGraphicPacksWindow.h | 0 src/gui/{ => wxGui}/GameProfileWindow.cpp | 4 +- src/gui/{ => wxGui}/GameProfileWindow.h | 0 src/gui/{ => wxGui}/GameUpdateWindow.cpp | 8 +- src/gui/{ => wxGui}/GameUpdateWindow.h | 0 src/gui/{ => wxGui}/GeneralSettings2.cpp | 12 +- src/gui/{ => wxGui}/GeneralSettings2.h | 0 src/gui/{ => wxGui}/GettingStartedDialog.cpp | 10 +- src/gui/{ => wxGui}/GettingStartedDialog.h | 0 src/gui/{ => wxGui}/GraphicPacksWindow2.cpp | 6 +- src/gui/{ => wxGui}/GraphicPacksWindow2.h | 0 src/gui/{ => wxGui}/LoggingWindow.cpp | 6 +- src/gui/{ => wxGui}/LoggingWindow.h | 2 +- src/gui/{ => wxGui}/MainWindow.cpp | 48 ++--- src/gui/{ => wxGui}/MainWindow.h | 8 +- src/gui/{ => wxGui}/MemorySearcherTool.cpp | 6 +- src/gui/{ => wxGui}/MemorySearcherTool.h | 2 +- src/gui/{ => wxGui}/PadViewFrame.cpp | 14 +- src/gui/{ => wxGui}/PadViewFrame.h | 0 src/gui/{ => wxGui}/TitleManager.cpp | 22 +-- src/gui/{ => wxGui}/TitleManager.h | 0 src/gui/{ => wxGui}/canvas/IRenderCanvas.h | 0 src/gui/{ => wxGui}/canvas/OpenGLCanvas.cpp | 4 +- src/gui/wxGui/canvas/OpenGLCanvas.h | 5 + src/gui/{ => wxGui}/canvas/VulkanCanvas.cpp | 6 +- src/gui/{ => wxGui}/canvas/VulkanCanvas.h | 2 +- src/gui/{ => wxGui}/components/TextList.cpp | 2 +- src/gui/{ => wxGui}/components/TextList.h | 0 .../components/wxDownloadManagerList.cpp | 12 +- .../components/wxDownloadManagerList.h | 2 +- src/gui/{ => wxGui}/components/wxGameList.cpp | 12 +- src/gui/{ => wxGui}/components/wxGameList.h | 0 .../{ => wxGui}/components/wxInputDraw.cpp | 2 +- src/gui/{ => wxGui}/components/wxInputDraw.h | 0 src/gui/{ => wxGui}/components/wxLogCtrl.cpp | 2 +- src/gui/{ => wxGui}/components/wxLogCtrl.h | 0 .../components/wxTitleManagerList.cpp | 14 +- .../components/wxTitleManagerList.h | 2 +- .../debugger/AudioDebuggerWindow.cpp | 4 +- .../debugger/AudioDebuggerWindow.h | 0 .../{ => wxGui}/debugger/BreakpointWindow.cpp | 8 +- .../{ => wxGui}/debugger/BreakpointWindow.h | 2 +- .../{ => wxGui}/debugger/DebuggerWindow2.cpp | 16 +- .../{ => wxGui}/debugger/DebuggerWindow2.h | 2 +- src/gui/{ => wxGui}/debugger/DisasmCtrl.cpp | 8 +- src/gui/{ => wxGui}/debugger/DisasmCtrl.h | 2 +- src/gui/{ => wxGui}/debugger/DumpCtrl.cpp | 4 +- src/gui/{ => wxGui}/debugger/DumpCtrl.h | 2 +- src/gui/{ => wxGui}/debugger/DumpWindow.cpp | 8 +- src/gui/{ => wxGui}/debugger/DumpWindow.h | 2 +- src/gui/{ => wxGui}/debugger/ModuleWindow.cpp | 8 +- src/gui/{ => wxGui}/debugger/ModuleWindow.h | 0 src/gui/{ => wxGui}/debugger/RegisterCtrl.cpp | 6 +- src/gui/{ => wxGui}/debugger/RegisterCtrl.h | 2 +- .../{ => wxGui}/debugger/RegisterWindow.cpp | 6 +- src/gui/{ => wxGui}/debugger/RegisterWindow.h | 0 src/gui/{ => wxGui}/debugger/SymbolCtrl.cpp | 4 +- src/gui/{ => wxGui}/debugger/SymbolCtrl.h | 0 src/gui/{ => wxGui}/debugger/SymbolWindow.cpp | 8 +- src/gui/{ => wxGui}/debugger/SymbolWindow.h | 2 +- .../CreateAccount/wxCreateAccountDialog.cpp | 2 +- .../CreateAccount/wxCreateAccountDialog.h | 0 .../dialogs/SaveImport/SaveImportWindow.cpp | 2 +- .../dialogs/SaveImport/SaveImportWindow.h | 0 .../dialogs/SaveImport/SaveTransfer.cpp | 2 +- .../dialogs/SaveImport/SaveTransfer.h | 0 src/gui/{ => wxGui}/helpers/wxControlObject.h | 0 src/gui/{ => wxGui}/helpers/wxCustomData.h | 0 .../{ => wxGui}/helpers/wxCustomEvents.cpp | 2 +- src/gui/{ => wxGui}/helpers/wxCustomEvents.h | 0 src/gui/{ => wxGui}/helpers/wxHelpers.cpp | 4 +- src/gui/{ => wxGui}/helpers/wxHelpers.h | 0 src/gui/{ => wxGui}/helpers/wxLogEvent.h | 0 src/gui/{ => wxGui}/helpers/wxWayland.cpp | 2 +- src/gui/{ => wxGui}/helpers/wxWayland.h | 0 .../{ => wxGui}/input/InputAPIAddWindow.cpp | 6 +- src/gui/{ => wxGui}/input/InputAPIAddWindow.h | 2 +- src/gui/{ => wxGui}/input/InputSettings2.cpp | 22 +-- src/gui/{ => wxGui}/input/InputSettings2.h | 0 .../panels/ClassicControllerInputPanel.cpp | 8 +- .../panels/ClassicControllerInputPanel.h | 2 +- .../{ => wxGui}/input/panels/InputPanel.cpp | 4 +- src/gui/{ => wxGui}/input/panels/InputPanel.h | 2 +- .../input/panels/ProControllerInputPanel.cpp | 6 +- .../input/panels/ProControllerInputPanel.h | 4 +- .../input/panels/VPADInputPanel.cpp | 8 +- .../{ => wxGui}/input/panels/VPADInputPanel.h | 2 +- .../input/panels/WiimoteInputPanel.cpp | 8 +- .../input/panels/WiimoteInputPanel.h | 2 +- .../settings/DefaultControllerSettings.cpp | 10 +- .../settings/DefaultControllerSettings.h | 0 .../settings/WiimoteControllerSettings.cpp | 10 +- .../settings/WiimoteControllerSettings.h | 0 .../DebugPPCThreadsWindow.cpp | 2 +- .../PPCThreadsViewer/DebugPPCThreadsWindow.h | 0 .../TextureRelationWindow.cpp | 2 +- .../TextureRelationWindow.h | 0 .../wxGuiWrapper.cpp} | 79 +++++++- src/gui/{ => wxGui}/wxHelper.h | 0 src/gui/{ => wxGui}/wxcomponents/checked.xpm | 0 src/gui/{ => wxGui}/wxcomponents/checked2.xpm | 0 .../{ => wxGui}/wxcomponents/checked_d.xpm | 0 .../{ => wxGui}/wxcomponents/checked_dis.xpm | 0 .../{ => wxGui}/wxcomponents/checked_ld.xpm | 0 .../{ => wxGui}/wxcomponents/checked_mo.xpm | 0 .../wxcomponents/checkedlistctrl.cpp | 10 +- .../wxcomponents/checkedlistctrl.h | 0 .../{ => wxGui}/wxcomponents/checktree.cpp | 18 +- src/gui/{ => wxGui}/wxcomponents/checktree.h | 0 .../{ => wxGui}/wxcomponents/unchecked.xpm | 0 .../{ => wxGui}/wxcomponents/unchecked2.xpm | 0 .../{ => wxGui}/wxcomponents/unchecked_d.xpm | 0 .../wxcomponents/unchecked_dis.xpm | 0 .../{ => wxGui}/wxcomponents/unchecked_ld.xpm | 0 .../{ => wxGui}/wxcomponents/unchecked_mo.xpm | 0 src/gui/{ => wxGui}/wxgui.h | 2 - 126 files changed, 491 insertions(+), 424 deletions(-) delete mode 100644 src/gui/canvas/OpenGLCanvas.h create mode 100644 src/gui/wxGui/CMakeLists.txt rename src/gui/{ => wxGui}/CemuApp.cpp (98%) rename src/gui/{ => wxGui}/CemuApp.h (100%) rename src/gui/{ => wxGui}/CemuUpdateWindow.cpp (99%) rename src/gui/{ => wxGui}/CemuUpdateWindow.h (100%) rename src/gui/{ => wxGui}/ChecksumTool.cpp (99%) rename src/gui/{ => wxGui}/ChecksumTool.h (96%) rename src/gui/{ => wxGui}/DownloadGraphicPacksWindow.cpp (99%) rename src/gui/{ => wxGui}/DownloadGraphicPacksWindow.h (100%) rename src/gui/{ => wxGui}/GameProfileWindow.cpp (99%) rename src/gui/{ => wxGui}/GameProfileWindow.h (100%) rename src/gui/{ => wxGui}/GameUpdateWindow.cpp (98%) rename src/gui/{ => wxGui}/GameUpdateWindow.h (100%) rename src/gui/{ => wxGui}/GeneralSettings2.cpp (99%) rename src/gui/{ => wxGui}/GeneralSettings2.h (100%) rename src/gui/{ => wxGui}/GettingStartedDialog.cpp (98%) rename src/gui/{ => wxGui}/GettingStartedDialog.h (100%) rename src/gui/{ => wxGui}/GraphicPacksWindow2.cpp (99%) rename src/gui/{ => wxGui}/GraphicPacksWindow2.h (100%) rename src/gui/{ => wxGui}/LoggingWindow.cpp (96%) rename src/gui/{ => wxGui}/LoggingWindow.h (96%) rename src/gui/{ => wxGui}/MainWindow.cpp (98%) rename src/gui/{ => wxGui}/MainWindow.h (97%) rename src/gui/{ => wxGui}/MemorySearcherTool.cpp (99%) rename src/gui/{ => wxGui}/MemorySearcherTool.h (99%) rename src/gui/{ => wxGui}/PadViewFrame.cpp (96%) rename src/gui/{ => wxGui}/PadViewFrame.h (100%) rename src/gui/{ => wxGui}/TitleManager.cpp (98%) rename src/gui/{ => wxGui}/TitleManager.h (100%) rename src/gui/{ => wxGui}/canvas/IRenderCanvas.h (100%) rename src/gui/{ => wxGui}/canvas/OpenGLCanvas.cpp (97%) create mode 100644 src/gui/wxGui/canvas/OpenGLCanvas.h rename src/gui/{ => wxGui}/canvas/VulkanCanvas.cpp (95%) rename src/gui/{ => wxGui}/canvas/VulkanCanvas.h (92%) rename src/gui/{ => wxGui}/components/TextList.cpp (99%) rename src/gui/{ => wxGui}/components/TextList.h (100%) rename src/gui/{ => wxGui}/components/wxDownloadManagerList.cpp (98%) rename src/gui/{ => wxGui}/components/wxDownloadManagerList.h (99%) rename src/gui/{ => wxGui}/components/wxGameList.cpp (99%) rename src/gui/{ => wxGui}/components/wxGameList.h (100%) rename src/gui/{ => wxGui}/components/wxInputDraw.cpp (98%) rename src/gui/{ => wxGui}/components/wxInputDraw.h (100%) rename src/gui/{ => wxGui}/components/wxLogCtrl.cpp (98%) rename src/gui/{ => wxGui}/components/wxLogCtrl.h (100%) rename src/gui/{ => wxGui}/components/wxTitleManagerList.cpp (99%) rename src/gui/{ => wxGui}/components/wxTitleManagerList.h (99%) rename src/gui/{ => wxGui}/debugger/AudioDebuggerWindow.cpp (99%) rename src/gui/{ => wxGui}/debugger/AudioDebuggerWindow.h (100%) rename src/gui/{ => wxGui}/debugger/BreakpointWindow.cpp (98%) rename src/gui/{ => wxGui}/debugger/BreakpointWindow.h (92%) rename src/gui/{ => wxGui}/debugger/DebuggerWindow2.cpp (98%) rename src/gui/{ => wxGui}/debugger/DebuggerWindow2.h (98%) rename src/gui/{ => wxGui}/debugger/DisasmCtrl.cpp (99%) rename src/gui/{ => wxGui}/debugger/DisasmCtrl.h (97%) rename src/gui/{ => wxGui}/debugger/DumpCtrl.cpp (99%) rename src/gui/{ => wxGui}/debugger/DumpCtrl.h (95%) rename src/gui/{ => wxGui}/debugger/DumpWindow.cpp (90%) rename src/gui/{ => wxGui}/debugger/DumpWindow.h (90%) rename src/gui/{ => wxGui}/debugger/ModuleWindow.cpp (96%) rename src/gui/{ => wxGui}/debugger/ModuleWindow.h (100%) rename src/gui/{ => wxGui}/debugger/RegisterCtrl.cpp (98%) rename src/gui/{ => wxGui}/debugger/RegisterCtrl.h (93%) rename src/gui/{ => wxGui}/debugger/RegisterWindow.cpp (99%) rename src/gui/{ => wxGui}/debugger/RegisterWindow.h (100%) rename src/gui/{ => wxGui}/debugger/SymbolCtrl.cpp (98%) rename src/gui/{ => wxGui}/debugger/SymbolCtrl.h (100%) rename src/gui/{ => wxGui}/debugger/SymbolWindow.cpp (91%) rename src/gui/{ => wxGui}/debugger/SymbolWindow.h (92%) rename src/gui/{ => wxGui}/dialogs/CreateAccount/wxCreateAccountDialog.cpp (98%) rename src/gui/{ => wxGui}/dialogs/CreateAccount/wxCreateAccountDialog.h (100%) rename src/gui/{ => wxGui}/dialogs/SaveImport/SaveImportWindow.cpp (99%) rename src/gui/{ => wxGui}/dialogs/SaveImport/SaveImportWindow.h (100%) rename src/gui/{ => wxGui}/dialogs/SaveImport/SaveTransfer.cpp (99%) rename src/gui/{ => wxGui}/dialogs/SaveImport/SaveTransfer.h (100%) rename src/gui/{ => wxGui}/helpers/wxControlObject.h (100%) rename src/gui/{ => wxGui}/helpers/wxCustomData.h (100%) rename src/gui/{ => wxGui}/helpers/wxCustomEvents.cpp (87%) rename src/gui/{ => wxGui}/helpers/wxCustomEvents.h (100%) rename src/gui/{ => wxGui}/helpers/wxHelpers.cpp (95%) rename src/gui/{ => wxGui}/helpers/wxHelpers.h (100%) rename src/gui/{ => wxGui}/helpers/wxLogEvent.h (100%) rename src/gui/{ => wxGui}/helpers/wxWayland.cpp (95%) rename src/gui/{ => wxGui}/helpers/wxWayland.h (100%) rename src/gui/{ => wxGui}/input/InputAPIAddWindow.cpp (98%) rename src/gui/{ => wxGui}/input/InputAPIAddWindow.h (97%) rename src/gui/{ => wxGui}/input/InputSettings2.cpp (98%) rename src/gui/{ => wxGui}/input/InputSettings2.h (100%) rename src/gui/{ => wxGui}/input/panels/ClassicControllerInputPanel.cpp (96%) rename src/gui/{ => wxGui}/input/panels/ClassicControllerInputPanel.h (90%) rename src/gui/{ => wxGui}/input/panels/InputPanel.cpp (98%) rename src/gui/{ => wxGui}/input/panels/InputPanel.h (98%) rename src/gui/{ => wxGui}/input/panels/ProControllerInputPanel.cpp (97%) rename src/gui/{ => wxGui}/input/panels/ProControllerInputPanel.h (85%) rename src/gui/{ => wxGui}/input/panels/VPADInputPanel.cpp (97%) rename src/gui/{ => wxGui}/input/panels/VPADInputPanel.h (93%) rename src/gui/{ => wxGui}/input/panels/WiimoteInputPanel.cpp (98%) rename src/gui/{ => wxGui}/input/panels/WiimoteInputPanel.h (95%) rename src/gui/{ => wxGui}/input/settings/DefaultControllerSettings.cpp (98%) rename src/gui/{ => wxGui}/input/settings/DefaultControllerSettings.h (100%) rename src/gui/{ => wxGui}/input/settings/WiimoteControllerSettings.cpp (98%) rename src/gui/{ => wxGui}/input/settings/WiimoteControllerSettings.h (100%) rename src/gui/{ => wxGui}/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp (99%) rename src/gui/{ => wxGui}/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h (100%) rename src/gui/{ => wxGui}/windows/TextureRelationViewer/TextureRelationWindow.cpp (99%) rename src/gui/{ => wxGui}/windows/TextureRelationViewer/TextureRelationWindow.h (100%) rename src/gui/{guiWrapper.cpp => wxGui/wxGuiWrapper.cpp} (85%) rename src/gui/{ => wxGui}/wxHelper.h (100%) rename src/gui/{ => wxGui}/wxcomponents/checked.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/checked2.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/checked_d.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/checked_dis.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/checked_ld.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/checked_mo.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/checkedlistctrl.cpp (98%) rename src/gui/{ => wxGui}/wxcomponents/checkedlistctrl.h (100%) rename src/gui/{ => wxGui}/wxcomponents/checktree.cpp (97%) rename src/gui/{ => wxGui}/wxcomponents/checktree.h (100%) rename src/gui/{ => wxGui}/wxcomponents/unchecked.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/unchecked2.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/unchecked_d.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/unchecked_dis.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/unchecked_ld.xpm (100%) rename src/gui/{ => wxGui}/wxcomponents/unchecked_mo.xpm (100%) rename src/gui/{ => wxGui}/wxgui.h (98%) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a1572c13..7efdda80 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,174 +1,8 @@ -add_library(CemuGui - canvas/IRenderCanvas.h - canvas/OpenGLCanvas.cpp - canvas/OpenGLCanvas.h - canvas/VulkanCanvas.cpp - canvas/VulkanCanvas.h - CemuApp.cpp - CemuApp.h - CemuUpdateWindow.cpp - CemuUpdateWindow.h - ChecksumTool.cpp - ChecksumTool.h - components/TextList.cpp - components/TextList.h - components/wxDownloadManagerList.cpp - components/wxDownloadManagerList.h - components/wxGameList.cpp - components/wxGameList.h - components/wxInputDraw.cpp - components/wxInputDraw.h - components/wxLogCtrl.cpp - components/wxLogCtrl.h - components/wxTitleManagerList.cpp - components/wxTitleManagerList.h - debugger/AudioDebuggerWindow.cpp - debugger/AudioDebuggerWindow.h - debugger/BreakpointWindow.cpp - debugger/BreakpointWindow.h - debugger/DebuggerWindow2.cpp - debugger/DebuggerWindow2.h - debugger/DisasmCtrl.cpp - debugger/DisasmCtrl.h - debugger/DumpCtrl.cpp - debugger/DumpCtrl.h - debugger/DumpWindow.cpp - debugger/DumpWindow.h - debugger/ModuleWindow.cpp - debugger/ModuleWindow.h - debugger/RegisterCtrl.cpp - debugger/RegisterCtrl.h - debugger/RegisterWindow.cpp - debugger/RegisterWindow.h - debugger/SymbolCtrl.cpp - debugger/SymbolCtrl.h - debugger/SymbolWindow.cpp - debugger/SymbolWindow.h - dialogs/CreateAccount/wxCreateAccountDialog.cpp - dialogs/CreateAccount/wxCreateAccountDialog.h - dialogs/SaveImport/SaveImportWindow.cpp - dialogs/SaveImport/SaveImportWindow.h - dialogs/SaveImport/SaveTransfer.cpp - dialogs/SaveImport/SaveTransfer.h - DownloadGraphicPacksWindow.cpp - DownloadGraphicPacksWindow.h - GameProfileWindow.cpp - GameProfileWindow.h - GameUpdateWindow.cpp - GameUpdateWindow.h - GeneralSettings2.cpp - GeneralSettings2.h - GettingStartedDialog.cpp - GettingStartedDialog.h - GraphicPacksWindow2.cpp - GraphicPacksWindow2.h - guiWrapper.cpp - guiWrapper.h - helpers/wxControlObject.h - helpers/wxCustomData.h - helpers/wxCustomEvents.cpp - helpers/wxCustomEvents.h - helpers/wxHelpers.cpp - helpers/wxHelpers.h - helpers/wxLogEvent.h - helpers/wxWayland.cpp - helpers/wxWayland.h - input/InputAPIAddWindow.cpp - input/InputAPIAddWindow.h - input/InputSettings2.cpp - input/InputSettings2.h - input/panels/ClassicControllerInputPanel.cpp - input/panels/ClassicControllerInputPanel.h - input/panels/InputPanel.cpp - input/panels/InputPanel.h - input/panels/ProControllerInputPanel.cpp - input/panels/ProControllerInputPanel.h - input/panels/VPADInputPanel.cpp - input/panels/VPADInputPanel.h - input/panels/WiimoteInputPanel.cpp - input/panels/WiimoteInputPanel.h - input/settings/DefaultControllerSettings.cpp - input/settings/DefaultControllerSettings.h - input/settings/WiimoteControllerSettings.cpp - input/settings/WiimoteControllerSettings.h - LoggingWindow.cpp - LoggingWindow.h - MainWindow.cpp - MainWindow.h - MemorySearcherTool.cpp - MemorySearcherTool.h - PadViewFrame.cpp - PadViewFrame.h - TitleManager.cpp - TitleManager.h - windows/PPCThreadsViewer - windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp - windows/PPCThreadsViewer/DebugPPCThreadsWindow.h - windows/TextureRelationViewer - windows/TextureRelationViewer/TextureRelationWindow.cpp - windows/TextureRelationViewer/TextureRelationWindow.h - wxcomponents/checked2.xpm - wxcomponents/checked_dis.xpm - wxcomponents/checked_d.xpm - wxcomponents/checked_ld.xpm - wxcomponents/checkedlistctrl.cpp - wxcomponents/checkedlistctrl.h - wxcomponents/checked_mo.xpm - wxcomponents/checked.xpm - wxcomponents/checktree.cpp - wxcomponents/checktree.h - wxcomponents/unchecked2.xpm - wxcomponents/unchecked_dis.xpm - wxcomponents/unchecked_d.xpm - wxcomponents/unchecked_ld.xpm - wxcomponents/unchecked_mo.xpm - wxcomponents/unchecked.xpm - wxgui.h - wxHelper.h +add_library(CemuGui + guiWrapper.h ) -set_property(TARGET CemuGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - - -target_include_directories(CemuGui PUBLIC "../") -# PUBLIC because rapidjson/document.h is included in ChecksumTool.h -target_include_directories(CemuGui PUBLIC ${RAPIDJSON_INCLUDE_DIRS}) - -target_link_libraries(CemuGui PRIVATE - CemuAudio - CemuCafe - CemuCommon - CemuComponents - CemuConfig - CemuInput - CemuResource - CemuUtil - Boost::headers - CURL::libcurl - libzip::zip - OpenSSL::Crypto - pugixml::pugixml - ZArchive::zarchive -) - -if(ENABLE_WXWIDGETS AND UNIX AND NOT APPLE) - # PUBLIC because gdk/gdkkeysyms.h is included in guiWrapper.h - target_link_libraries(CemuGui PUBLIC GTK3::gtk) - if (ENABLE_WAYLAND) - target_link_libraries(CemuGui PRIVATE Wayland::Client CemuWaylandProtocols) - endif() -endif() - -if(ENABLE_CUBEB) - target_link_libraries(CemuGui PRIVATE cubeb::cubeb) -endif() - -if(UNIX AND NOT APPLE) - if(ENABLE_FERAL_GAMEMODE) - target_link_libraries(CemuGui PRIVATE gamemode) - endif() -endif() -if (ENABLE_WXWIDGETS) - # PUBLIC because wx/app.h is included in CemuApp.h - target_link_libraries(CemuGui PUBLIC wx::base wx::core wx::gl wx::propgrid wx::xrc) +if(ENABLE_WXWIDGETS) + add_subdirectory(wxGui) + target_link_libraries(CemuGui PRIVATE CemuwxGui) endif() diff --git a/src/gui/canvas/OpenGLCanvas.h b/src/gui/canvas/OpenGLCanvas.h deleted file mode 100644 index 282e6d19..00000000 --- a/src/gui/canvas/OpenGLCanvas.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once -#include -#include "gui/canvas/IRenderCanvas.h" - -wxWindow* GLCanvas_Create(wxWindow* parent, const wxSize& size, bool is_main_window); -bool GLCanvas_MakeCurrent(bool padView); -void GLCanvas_SwapBuffers(bool swapTV, bool swapDRC); -bool GLCanvas_HasPadViewOpen(); \ No newline at end of file diff --git a/src/gui/wxGui/CMakeLists.txt b/src/gui/wxGui/CMakeLists.txt new file mode 100644 index 00000000..1a1b1d58 --- /dev/null +++ b/src/gui/wxGui/CMakeLists.txt @@ -0,0 +1,171 @@ +add_library(CemuwxGui + canvas/IRenderCanvas.h + canvas/OpenGLCanvas.cpp + canvas/OpenGLCanvas.h + canvas/VulkanCanvas.cpp + canvas/VulkanCanvas.h + CemuApp.cpp + CemuApp.h + CemuUpdateWindow.cpp + CemuUpdateWindow.h + ChecksumTool.cpp + ChecksumTool.h + components/TextList.cpp + components/TextList.h + components/wxDownloadManagerList.cpp + components/wxDownloadManagerList.h + components/wxGameList.cpp + components/wxGameList.h + components/wxInputDraw.cpp + components/wxInputDraw.h + components/wxLogCtrl.cpp + components/wxLogCtrl.h + components/wxTitleManagerList.cpp + components/wxTitleManagerList.h + debugger/AudioDebuggerWindow.cpp + debugger/AudioDebuggerWindow.h + debugger/BreakpointWindow.cpp + debugger/BreakpointWindow.h + debugger/DebuggerWindow2.cpp + debugger/DebuggerWindow2.h + debugger/DisasmCtrl.cpp + debugger/DisasmCtrl.h + debugger/DumpCtrl.cpp + debugger/DumpCtrl.h + debugger/DumpWindow.cpp + debugger/DumpWindow.h + debugger/ModuleWindow.cpp + debugger/ModuleWindow.h + debugger/RegisterCtrl.cpp + debugger/RegisterCtrl.h + debugger/RegisterWindow.cpp + debugger/RegisterWindow.h + debugger/SymbolCtrl.cpp + debugger/SymbolCtrl.h + debugger/SymbolWindow.cpp + debugger/SymbolWindow.h + dialogs/CreateAccount/wxCreateAccountDialog.cpp + dialogs/CreateAccount/wxCreateAccountDialog.h + dialogs/SaveImport/SaveImportWindow.cpp + dialogs/SaveImport/SaveImportWindow.h + dialogs/SaveImport/SaveTransfer.cpp + dialogs/SaveImport/SaveTransfer.h + DownloadGraphicPacksWindow.cpp + DownloadGraphicPacksWindow.h + GameProfileWindow.cpp + GameProfileWindow.h + GameUpdateWindow.cpp + GameUpdateWindow.h + GeneralSettings2.cpp + GeneralSettings2.h + GettingStartedDialog.cpp + GettingStartedDialog.h + GraphicPacksWindow2.cpp + GraphicPacksWindow2.h + helpers/wxControlObject.h + helpers/wxCustomData.h + helpers/wxCustomEvents.cpp + helpers/wxCustomEvents.h + helpers/wxHelpers.cpp + helpers/wxHelpers.h + helpers/wxLogEvent.h + helpers/wxWayland.cpp + helpers/wxWayland.h + input/InputAPIAddWindow.cpp + input/InputAPIAddWindow.h + input/InputSettings2.cpp + input/InputSettings2.h + input/panels/ClassicControllerInputPanel.cpp + input/panels/ClassicControllerInputPanel.h + input/panels/InputPanel.cpp + input/panels/InputPanel.h + input/panels/ProControllerInputPanel.cpp + input/panels/ProControllerInputPanel.h + input/panels/VPADInputPanel.cpp + input/panels/VPADInputPanel.h + input/panels/WiimoteInputPanel.cpp + input/panels/WiimoteInputPanel.h + input/settings/DefaultControllerSettings.cpp + input/settings/DefaultControllerSettings.h + input/settings/WiimoteControllerSettings.cpp + input/settings/WiimoteControllerSettings.h + LoggingWindow.cpp + LoggingWindow.h + MainWindow.cpp + MainWindow.h + MemorySearcherTool.cpp + MemorySearcherTool.h + PadViewFrame.cpp + PadViewFrame.h + TitleManager.cpp + TitleManager.h + windows/PPCThreadsViewer + windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp + windows/PPCThreadsViewer/DebugPPCThreadsWindow.h + windows/TextureRelationViewer + windows/TextureRelationViewer/TextureRelationWindow.cpp + windows/TextureRelationViewer/TextureRelationWindow.h + wxcomponents/checked2.xpm + wxcomponents/checked_dis.xpm + wxcomponents/checked_d.xpm + wxcomponents/checked_ld.xpm + wxcomponents/checkedlistctrl.cpp + wxcomponents/checkedlistctrl.h + wxcomponents/checked_mo.xpm + wxcomponents/checked.xpm + wxcomponents/checktree.cpp + wxcomponents/checktree.h + wxcomponents/unchecked2.xpm + wxcomponents/unchecked_dis.xpm + wxcomponents/unchecked_d.xpm + wxcomponents/unchecked_ld.xpm + wxcomponents/unchecked_mo.xpm + wxcomponents/unchecked.xpm + wxgui.h + wxGuiWrapper.cpp + wxHelper.h +) + +set_property(TARGET CemuwxGui PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + + +target_include_directories(CemuwxGui PUBLIC "../") +# PUBLIC because rapidjson/document.h is included in ChecksumTool.h +target_include_directories(CemuwxGui PUBLIC ${RAPIDJSON_INCLUDE_DIRS}) + +target_link_libraries(CemuwxGui PRIVATE + CemuAudio + CemuCafe + CemuCommon + CemuComponents + CemuConfig + CemuInput + CemuResource + CemuUtil + Boost::headers + CURL::libcurl + libzip::zip + OpenSSL::Crypto + pugixml::pugixml + ZArchive::zarchive +) + +if(UNIX AND NOT APPLE) + target_link_libraries(CemuwxGui PRIVATE GTK3::gtk) + if (ENABLE_WAYLAND) + target_link_libraries(CemuwxGui PRIVATE Wayland::Client CemuWaylandProtocols) + endif() +endif() + +if(ENABLE_CUBEB) + target_link_libraries(CemuwxGui PRIVATE cubeb::cubeb) +endif() + +if(UNIX AND NOT APPLE) + if(ENABLE_FERAL_GAMEMODE) + target_link_libraries(CemuwxGui PRIVATE gamemode) + endif() +endif() + +# PUBLIC because wx/app.h is included in CemuApp.h +target_link_libraries(CemuwxGui PUBLIC wx::base wx::core wx::gl wx::propgrid wx::xrc) diff --git a/src/gui/CemuApp.cpp b/src/gui/wxGui/CemuApp.cpp similarity index 98% rename from src/gui/CemuApp.cpp rename to src/gui/wxGui/CemuApp.cpp index 73cbcb75..b84e6604 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/wxGui/CemuApp.cpp @@ -1,19 +1,19 @@ -#include "gui/CemuApp.h" -#include "gui/MainWindow.h" -#include "gui/wxgui.h" +#include "CemuApp.h" +#include "MainWindow.h" +#include "wxgui.h" #include "config/CemuConfig.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" -#include "gui/guiWrapper.h" +#include "guiWrapper.h" #include "config/ActiveSettings.h" -#include "gui/GettingStartedDialog.h" +#include "GettingStartedDialog.h" #include "config/PermanentConfig.h" #include "config/PermanentStorage.h" #include "input/InputManager.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" #include "Cemu/ncrypto/ncrypto.h" #if BOOST_OS_LINUX && HAS_WAYLAND -#include "gui/helpers/wxWayland.h" +#include "helpers/wxWayland.h" #endif #include diff --git a/src/gui/CemuApp.h b/src/gui/wxGui/CemuApp.h similarity index 100% rename from src/gui/CemuApp.h rename to src/gui/wxGui/CemuApp.h diff --git a/src/gui/CemuUpdateWindow.cpp b/src/gui/wxGui/CemuUpdateWindow.cpp similarity index 99% rename from src/gui/CemuUpdateWindow.cpp rename to src/gui/wxGui/CemuUpdateWindow.cpp index f3568ee7..2fc6d9aa 100644 --- a/src/gui/CemuUpdateWindow.cpp +++ b/src/gui/wxGui/CemuUpdateWindow.cpp @@ -1,4 +1,4 @@ -#include "gui/CemuUpdateWindow.h" +#include "CemuUpdateWindow.h" #include "Common/version.h" #include "util/helpers/helpers.h" diff --git a/src/gui/CemuUpdateWindow.h b/src/gui/wxGui/CemuUpdateWindow.h similarity index 100% rename from src/gui/CemuUpdateWindow.h rename to src/gui/wxGui/CemuUpdateWindow.h diff --git a/src/gui/ChecksumTool.cpp b/src/gui/wxGui/ChecksumTool.cpp similarity index 99% rename from src/gui/ChecksumTool.cpp rename to src/gui/wxGui/ChecksumTool.cpp index 7dc61bb3..1993d70b 100644 --- a/src/gui/ChecksumTool.cpp +++ b/src/gui/wxGui/ChecksumTool.cpp @@ -1,10 +1,10 @@ -#include "gui/ChecksumTool.h" +#include "ChecksumTool.h" #include "Cafe/TitleList/GameInfo.h" -#include "gui/helpers/wxCustomEvents.h" +#include "helpers/wxCustomEvents.h" #include "util/helpers/helpers.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/wxHelper.h" +#include "helpers/wxHelpers.h" +#include "wxHelper.h" #include "Cafe/Filesystem/WUD/wud.h" #include diff --git a/src/gui/ChecksumTool.h b/src/gui/wxGui/ChecksumTool.h similarity index 96% rename from src/gui/ChecksumTool.h rename to src/gui/wxGui/ChecksumTool.h index 1b54733b..0081ab7f 100644 --- a/src/gui/ChecksumTool.h +++ b/src/gui/wxGui/ChecksumTool.h @@ -1,6 +1,6 @@ #pragma once #include -#include "gui/components/wxTitleManagerList.h" +#include "components/wxTitleManagerList.h" #include "Cafe/TitleList/TitleInfo.h" #include diff --git a/src/gui/DownloadGraphicPacksWindow.cpp b/src/gui/wxGui/DownloadGraphicPacksWindow.cpp similarity index 99% rename from src/gui/DownloadGraphicPacksWindow.cpp rename to src/gui/wxGui/DownloadGraphicPacksWindow.cpp index 8189b50a..543acf3b 100644 --- a/src/gui/DownloadGraphicPacksWindow.cpp +++ b/src/gui/wxGui/DownloadGraphicPacksWindow.cpp @@ -1,5 +1,5 @@ -#include "gui/wxgui.h" -#include "gui/DownloadGraphicPacksWindow.h" +#include "wxgui.h" +#include "DownloadGraphicPacksWindow.h" #include #include diff --git a/src/gui/DownloadGraphicPacksWindow.h b/src/gui/wxGui/DownloadGraphicPacksWindow.h similarity index 100% rename from src/gui/DownloadGraphicPacksWindow.h rename to src/gui/wxGui/DownloadGraphicPacksWindow.h diff --git a/src/gui/GameProfileWindow.cpp b/src/gui/wxGui/GameProfileWindow.cpp similarity index 99% rename from src/gui/GameProfileWindow.cpp rename to src/gui/wxGui/GameProfileWindow.cpp index 4d56e9cd..4d572ac3 100644 --- a/src/gui/GameProfileWindow.cpp +++ b/src/gui/wxGui/GameProfileWindow.cpp @@ -1,4 +1,4 @@ -#include "gui/GameProfileWindow.h" +#include "GameProfileWindow.h" #include #include @@ -8,7 +8,7 @@ #include #include -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" #include "input/InputManager.h" #if BOOST_OS_LINUX || BOOST_OS_MACOS diff --git a/src/gui/GameProfileWindow.h b/src/gui/wxGui/GameProfileWindow.h similarity index 100% rename from src/gui/GameProfileWindow.h rename to src/gui/wxGui/GameProfileWindow.h diff --git a/src/gui/GameUpdateWindow.cpp b/src/gui/wxGui/GameUpdateWindow.cpp similarity index 98% rename from src/gui/GameUpdateWindow.cpp rename to src/gui/wxGui/GameUpdateWindow.cpp index 40bf546e..36afacc9 100644 --- a/src/gui/GameUpdateWindow.cpp +++ b/src/gui/wxGui/GameUpdateWindow.cpp @@ -1,13 +1,13 @@ -#include "gui/wxgui.h" -#include "gui/GameUpdateWindow.h" +#include "wxgui.h" +#include "GameUpdateWindow.h" #include "util/helpers/helpers.h" #include #include #include "util/helpers/SystemException.h" -#include "gui/CemuApp.h" +#include "CemuApp.h" #include "Cafe/TitleList/GameInfo.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" #include "wxHelper.h" std::string _GetTitleIdTypeStr(TitleId titleId) diff --git a/src/gui/GameUpdateWindow.h b/src/gui/wxGui/GameUpdateWindow.h similarity index 100% rename from src/gui/GameUpdateWindow.h rename to src/gui/wxGui/GameUpdateWindow.h diff --git a/src/gui/GeneralSettings2.cpp b/src/gui/wxGui/GeneralSettings2.cpp similarity index 99% rename from src/gui/GeneralSettings2.cpp rename to src/gui/wxGui/GeneralSettings2.cpp index ad767ad1..6f344163 100644 --- a/src/gui/GeneralSettings2.cpp +++ b/src/gui/wxGui/GeneralSettings2.cpp @@ -1,7 +1,7 @@ -#include "gui/wxgui.h" -#include "gui/GeneralSettings2.h" -#include "gui/CemuApp.h" -#include "gui/helpers/wxControlObject.h" +#include "wxgui.h" +#include "GeneralSettings2.h" +#include "CemuApp.h" +#include "helpers/wxControlObject.h" #include "util/helpers/helpers.h" @@ -31,7 +31,7 @@ #include #include "util/helpers/SystemException.h" -#include "gui/dialogs/CreateAccount/wxCreateAccountDialog.h" +#include "dialogs/CreateAccount/wxCreateAccountDialog.h" #include "config/PermanentStorage.h" #if BOOST_OS_WINDOWS @@ -40,7 +40,7 @@ #include "config/LaunchSettings.h" #include "config/ActiveSettings.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" #include "resource/embedded/resources.h" diff --git a/src/gui/GeneralSettings2.h b/src/gui/wxGui/GeneralSettings2.h similarity index 100% rename from src/gui/GeneralSettings2.h rename to src/gui/wxGui/GeneralSettings2.h diff --git a/src/gui/GettingStartedDialog.cpp b/src/gui/wxGui/GettingStartedDialog.cpp similarity index 98% rename from src/gui/GettingStartedDialog.cpp rename to src/gui/wxGui/GettingStartedDialog.cpp index c84582b8..999d760f 100644 --- a/src/gui/GettingStartedDialog.cpp +++ b/src/gui/wxGui/GettingStartedDialog.cpp @@ -1,4 +1,4 @@ -#include "gui/GettingStartedDialog.h" +#include "GettingStartedDialog.h" #include #include @@ -6,10 +6,10 @@ #include #include "config/ActiveSettings.h" -#include "gui/CemuApp.h" -#include "gui/DownloadGraphicPacksWindow.h" -#include "gui/GraphicPacksWindow2.h" -#include "gui/input/InputSettings2.h" +#include "CemuApp.h" +#include "DownloadGraphicPacksWindow.h" +#include "GraphicPacksWindow2.h" +#include "input/InputSettings2.h" #include "config/CemuConfig.h" #include "config/PermanentConfig.h" diff --git a/src/gui/GettingStartedDialog.h b/src/gui/wxGui/GettingStartedDialog.h similarity index 100% rename from src/gui/GettingStartedDialog.h rename to src/gui/wxGui/GettingStartedDialog.h diff --git a/src/gui/GraphicPacksWindow2.cpp b/src/gui/wxGui/GraphicPacksWindow2.cpp similarity index 99% rename from src/gui/GraphicPacksWindow2.cpp rename to src/gui/wxGui/GraphicPacksWindow2.cpp index 78fa6569..5f03e444 100644 --- a/src/gui/GraphicPacksWindow2.cpp +++ b/src/gui/wxGui/GraphicPacksWindow2.cpp @@ -1,6 +1,6 @@ -#include "gui/wxgui.h" -#include "gui/GraphicPacksWindow2.h" -#include "gui/DownloadGraphicPacksWindow.h" +#include "wxgui.h" +#include "GraphicPacksWindow2.h" +#include "DownloadGraphicPacksWindow.h" #include "Cafe/GraphicPack/GraphicPack2.h" #include "config/CemuConfig.h" #include "config/ActiveSettings.h" diff --git a/src/gui/GraphicPacksWindow2.h b/src/gui/wxGui/GraphicPacksWindow2.h similarity index 100% rename from src/gui/GraphicPacksWindow2.h rename to src/gui/wxGui/GraphicPacksWindow2.h diff --git a/src/gui/LoggingWindow.cpp b/src/gui/wxGui/LoggingWindow.cpp similarity index 96% rename from src/gui/LoggingWindow.cpp rename to src/gui/wxGui/LoggingWindow.cpp index dbc7536d..4e0ae2ff 100644 --- a/src/gui/LoggingWindow.cpp +++ b/src/gui/wxGui/LoggingWindow.cpp @@ -1,12 +1,12 @@ -#include "gui/LoggingWindow.h" +#include "LoggingWindow.h" -#include "gui/helpers/wxLogEvent.h" +#include "helpers/wxLogEvent.h" #include #include #include -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" wxDEFINE_EVENT(EVT_LOG, wxLogEvent); diff --git a/src/gui/LoggingWindow.h b/src/gui/wxGui/LoggingWindow.h similarity index 96% rename from src/gui/LoggingWindow.h rename to src/gui/wxGui/LoggingWindow.h index 28ca30dd..ee27e975 100644 --- a/src/gui/LoggingWindow.h +++ b/src/gui/wxGui/LoggingWindow.h @@ -3,7 +3,7 @@ #include #include #include -#include "gui/components/wxLogCtrl.h" +#include "components/wxLogCtrl.h" class wxLogEvent; diff --git a/src/gui/MainWindow.cpp b/src/gui/wxGui/MainWindow.cpp similarity index 98% rename from src/gui/MainWindow.cpp rename to src/gui/wxGui/MainWindow.cpp index 852e32a7..b867494b 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/wxGui/MainWindow.cpp @@ -1,50 +1,50 @@ -#include "gui/wxgui.h" -#include "gui/MainWindow.h" -#include "gui/guiWrapper.h" +#include "wxgui.h" +#include "MainWindow.h" +#include "guiWrapper.h" #include #include -#include "gui/GameUpdateWindow.h" -#include "gui/PadViewFrame.h" -#include "gui/windows/TextureRelationViewer/TextureRelationWindow.h" -#include "gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h" -#include "gui/canvas/OpenGLCanvas.h" -#include "gui/canvas/VulkanCanvas.h" +#include "GameUpdateWindow.h" +#include "PadViewFrame.h" +#include "windows/TextureRelationViewer/TextureRelationWindow.h" +#include "windows/PPCThreadsViewer/DebugPPCThreadsWindow.h" +#include "canvas/OpenGLCanvas.h" +#include "canvas/VulkanCanvas.h" #include "Cafe/OS/libs/nn_nfp/nn_nfp.h" #include "Cafe/OS/libs/swkbd/swkbd.h" #include "Cafe/IOSU/legacy/iosu_crypto.h" #include "Cafe/GameProfile/GameProfile.h" -#include "gui/debugger/DebuggerWindow2.h" -#include "gui/debugger/AudioDebuggerWindow.h" +#include "debugger/DebuggerWindow2.h" +#include "debugger/AudioDebuggerWindow.h" #include "util/helpers/helpers.h" #include "config/CemuConfig.h" #include "Cemu/DiscordPresence/DiscordPresence.h" #include "util/ScreenSaver/ScreenSaver.h" -#include "gui/GeneralSettings2.h" -#include "gui/GraphicPacksWindow2.h" -#include "gui/GameProfileWindow.h" -#include "gui/CemuApp.h" -#include "gui/CemuUpdateWindow.h" -#include "gui/helpers/wxCustomData.h" -#include "gui/LoggingWindow.h" +#include "GeneralSettings2.h" +#include "GraphicPacksWindow2.h" +#include "GameProfileWindow.h" +#include "CemuApp.h" +#include "CemuUpdateWindow.h" +#include "helpers/wxCustomData.h" +#include "LoggingWindow.h" #include "config/ActiveSettings.h" #include "config/LaunchSettings.h" #include "Cafe/Filesystem/FST/FST.h" -#include "gui/TitleManager.h" +#include "TitleManager.h" #include "Cafe/CafeSystem.h" #include "Cafe/TitleList/GameInfo.h" #include #include "util/helpers/SystemException.h" -#include "gui/DownloadGraphicPacksWindow.h" -#include "gui/GettingStartedDialog.h" -#include "gui/helpers/wxHelpers.h" +#include "DownloadGraphicPacksWindow.h" +#include "GettingStartedDialog.h" +#include "helpers/wxHelpers.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h" -#include "gui/input/InputSettings2.h" +#include "input/InputSettings2.h" #include "input/InputManager.h" #if BOOST_OS_WINDOWS @@ -58,7 +58,7 @@ #endif #if BOOST_OS_LINUX && HAS_WAYLAND -#include "gui/helpers/wxWayland.h" +#include "helpers/wxWayland.h" #endif //GameMode support diff --git a/src/gui/MainWindow.h b/src/gui/wxGui/MainWindow.h similarity index 97% rename from src/gui/MainWindow.h rename to src/gui/wxGui/MainWindow.h index 735f73af..d49b6b30 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/wxGui/MainWindow.h @@ -5,13 +5,13 @@ #include #include "wxcomponents/checkedlistctrl.h" -#include "gui/PadViewFrame.h" -#include "gui/MemorySearcherTool.h" +#include "PadViewFrame.h" +#include "MemorySearcherTool.h" #include "config/XMLConfig.h" -#include "gui/LoggingWindow.h" -#include "gui/components/wxGameList.h" +#include "LoggingWindow.h" +#include "components/wxGameList.h" #include #include "Cafe/HW/Espresso/Debugger/GDBStub.h" diff --git a/src/gui/MemorySearcherTool.cpp b/src/gui/wxGui/MemorySearcherTool.cpp similarity index 99% rename from src/gui/MemorySearcherTool.cpp rename to src/gui/wxGui/MemorySearcherTool.cpp index 093f7ffe..5f791f3f 100644 --- a/src/gui/MemorySearcherTool.cpp +++ b/src/gui/wxGui/MemorySearcherTool.cpp @@ -1,13 +1,13 @@ -#include "gui/wxgui.h" +#include "wxgui.h" -#include "gui/MemorySearcherTool.h" +#include "MemorySearcherTool.h" #include #include #include #include "config/ActiveSettings.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" #include "Common/FileStream.h" #include "util/IniParser/IniParser.h" #include "util/helpers/StringHelpers.h" diff --git a/src/gui/MemorySearcherTool.h b/src/gui/wxGui/MemorySearcherTool.h similarity index 99% rename from src/gui/MemorySearcherTool.h rename to src/gui/wxGui/MemorySearcherTool.h index add9aced..98e2bc0b 100644 --- a/src/gui/MemorySearcherTool.h +++ b/src/gui/wxGui/MemorySearcherTool.h @@ -6,7 +6,7 @@ #include "Cafe/HW/MMU/MMU.h" #include "util/helpers/helpers.h" -#include "gui/helpers/wxCustomEvents.h" +#include "helpers/wxCustomEvents.h" enum SearchDataType { diff --git a/src/gui/PadViewFrame.cpp b/src/gui/wxGui/PadViewFrame.cpp similarity index 96% rename from src/gui/PadViewFrame.cpp rename to src/gui/wxGui/PadViewFrame.cpp index 4db25c5f..83b0731e 100644 --- a/src/gui/PadViewFrame.cpp +++ b/src/gui/wxGui/PadViewFrame.cpp @@ -1,16 +1,16 @@ -#include "gui/wxgui.h" -#include "gui/guiWrapper.h" -#include "gui/PadViewFrame.h" +#include "wxgui.h" +#include "guiWrapper.h" +#include "PadViewFrame.h" #include #include "config/ActiveSettings.h" #include "Cafe/OS/libs/swkbd/swkbd.h" -#include "gui/canvas/OpenGLCanvas.h" -#include "gui/canvas/VulkanCanvas.h" +#include "canvas/OpenGLCanvas.h" +#include "canvas/VulkanCanvas.h" #include "config/CemuConfig.h" -#include "gui/MainWindow.h" -#include "gui/helpers/wxHelpers.h" +#include "MainWindow.h" +#include "helpers/wxHelpers.h" #include "input/InputManager.h" #if BOOST_OS_LINUX || BOOST_OS_MACOS diff --git a/src/gui/PadViewFrame.h b/src/gui/wxGui/PadViewFrame.h similarity index 100% rename from src/gui/PadViewFrame.h rename to src/gui/wxGui/PadViewFrame.h diff --git a/src/gui/TitleManager.cpp b/src/gui/wxGui/TitleManager.cpp similarity index 98% rename from src/gui/TitleManager.cpp rename to src/gui/wxGui/TitleManager.cpp index 2440e12c..3eff5e97 100644 --- a/src/gui/TitleManager.cpp +++ b/src/gui/wxGui/TitleManager.cpp @@ -1,15 +1,15 @@ -#include "gui/TitleManager.h" +#include "TitleManager.h" -#include "gui/helpers/wxCustomEvents.h" -#include "gui/helpers/wxCustomData.h" +#include "helpers/wxCustomEvents.h" +#include "helpers/wxCustomData.h" #include "Cafe/TitleList/GameInfo.h" #include "util/helpers/helpers.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/wxHelper.h" -#include "gui/components/wxTitleManagerList.h" -#include "gui/components/wxDownloadManagerList.h" -#include "gui/GameUpdateWindow.h" -#include "gui/dialogs/SaveImport/SaveTransfer.h" +#include "helpers/wxHelpers.h" +#include "wxHelper.h" +#include "components/wxTitleManagerList.h" +#include "components/wxDownloadManagerList.h" +#include "GameUpdateWindow.h" +#include "dialogs/SaveImport/SaveTransfer.h" #include #include @@ -32,10 +32,10 @@ #include #include "config/ActiveSettings.h" -#include "gui/dialogs/SaveImport/SaveImportWindow.h" +#include "dialogs/SaveImport/SaveImportWindow.h" #include "Cafe/Account/Account.h" #include "Cemu/Tools/DownloadManager/DownloadManager.h" -#include "gui/CemuApp.h" +#include "CemuApp.h" #include "Cafe/TitleList/TitleList.h" diff --git a/src/gui/TitleManager.h b/src/gui/wxGui/TitleManager.h similarity index 100% rename from src/gui/TitleManager.h rename to src/gui/wxGui/TitleManager.h diff --git a/src/gui/canvas/IRenderCanvas.h b/src/gui/wxGui/canvas/IRenderCanvas.h similarity index 100% rename from src/gui/canvas/IRenderCanvas.h rename to src/gui/wxGui/canvas/IRenderCanvas.h diff --git a/src/gui/canvas/OpenGLCanvas.cpp b/src/gui/wxGui/canvas/OpenGLCanvas.cpp similarity index 97% rename from src/gui/canvas/OpenGLCanvas.cpp rename to src/gui/wxGui/canvas/OpenGLCanvas.cpp index 9f779863..7d9ec9c1 100644 --- a/src/gui/canvas/OpenGLCanvas.cpp +++ b/src/gui/wxGui/canvas/OpenGLCanvas.cpp @@ -1,8 +1,8 @@ -#include "gui/canvas/OpenGLCanvas.h" +#include "canvas/OpenGLCanvas.h" #include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h" #include "config/CemuConfig.h" - +#include "Cafe/HW/Latte/Renderer/OpenGL/GLCanvas.h" #include "Common/GLInclude/GLInclude.h" #include // this includes GL/gl.h, avoid using this in a header because it would contaminate our own OpenGL definitions (GLInclude) diff --git a/src/gui/wxGui/canvas/OpenGLCanvas.h b/src/gui/wxGui/canvas/OpenGLCanvas.h new file mode 100644 index 00000000..701a9a18 --- /dev/null +++ b/src/gui/wxGui/canvas/OpenGLCanvas.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include "canvas/IRenderCanvas.h" + +wxWindow* GLCanvas_Create(wxWindow* parent, const wxSize& size, bool is_main_window); diff --git a/src/gui/canvas/VulkanCanvas.cpp b/src/gui/wxGui/canvas/VulkanCanvas.cpp similarity index 95% rename from src/gui/canvas/VulkanCanvas.cpp rename to src/gui/wxGui/canvas/VulkanCanvas.cpp index ceddeb52..b73ce101 100644 --- a/src/gui/canvas/VulkanCanvas.cpp +++ b/src/gui/wxGui/canvas/VulkanCanvas.cpp @@ -1,9 +1,9 @@ -#include "gui/canvas/VulkanCanvas.h" +#include "canvas/VulkanCanvas.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h" -#include "gui/guiWrapper.h" +#include "guiWrapper.h" #if BOOST_OS_LINUX && HAS_WAYLAND -#include "gui/helpers/wxWayland.h" +#include "helpers/wxWayland.h" #endif #include diff --git a/src/gui/canvas/VulkanCanvas.h b/src/gui/wxGui/canvas/VulkanCanvas.h similarity index 92% rename from src/gui/canvas/VulkanCanvas.h rename to src/gui/wxGui/canvas/VulkanCanvas.h index 13d03a1e..691b768b 100644 --- a/src/gui/canvas/VulkanCanvas.h +++ b/src/gui/wxGui/canvas/VulkanCanvas.h @@ -1,6 +1,6 @@ #pragma once -#include "gui/canvas/IRenderCanvas.h" +#include "canvas/IRenderCanvas.h" #include diff --git a/src/gui/components/TextList.cpp b/src/gui/wxGui/components/TextList.cpp similarity index 99% rename from src/gui/components/TextList.cpp rename to src/gui/wxGui/components/TextList.cpp index 606c84f9..cb48be2b 100644 --- a/src/gui/components/TextList.cpp +++ b/src/gui/wxGui/components/TextList.cpp @@ -1,4 +1,4 @@ -#include "gui/wxgui.h" +#include "wxgui.h" #include "TextList.h" #include #include diff --git a/src/gui/components/TextList.h b/src/gui/wxGui/components/TextList.h similarity index 100% rename from src/gui/components/TextList.h rename to src/gui/wxGui/components/TextList.h diff --git a/src/gui/components/wxDownloadManagerList.cpp b/src/gui/wxGui/components/wxDownloadManagerList.cpp similarity index 98% rename from src/gui/components/wxDownloadManagerList.cpp rename to src/gui/wxGui/components/wxDownloadManagerList.cpp index ebff9e95..b8676bc4 100644 --- a/src/gui/components/wxDownloadManagerList.cpp +++ b/src/gui/wxGui/components/wxDownloadManagerList.cpp @@ -1,10 +1,10 @@ -#include "gui/components/wxDownloadManagerList.h" +#include "components/wxDownloadManagerList.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" #include "util/helpers/SystemException.h" #include "Cafe/TitleList/GameInfo.h" -#include "gui/components/wxGameList.h" -#include "gui/helpers/wxCustomEvents.h" +#include "components/wxGameList.h" +#include "helpers/wxCustomEvents.h" #include #include @@ -18,10 +18,10 @@ #include #include "config/ActiveSettings.h" -#include "gui/ChecksumTool.h" +#include "ChecksumTool.h" #include "Cemu/Tools/DownloadManager/DownloadManager.h" #include "Cafe/TitleList/TitleId.h" -#include "gui/MainWindow.h" +#include "MainWindow.h" wxDEFINE_EVENT(wxEVT_REMOVE_ENTRY, wxCommandEvent); diff --git a/src/gui/components/wxDownloadManagerList.h b/src/gui/wxGui/components/wxDownloadManagerList.h similarity index 99% rename from src/gui/components/wxDownloadManagerList.h rename to src/gui/wxGui/components/wxDownloadManagerList.h index 0af5b082..eed8c716 100644 --- a/src/gui/components/wxDownloadManagerList.h +++ b/src/gui/wxGui/components/wxDownloadManagerList.h @@ -1,6 +1,6 @@ #pragma once -#include "gui/helpers/wxCustomData.h" +#include "helpers/wxCustomData.h" #include "config/CemuConfig.h" #include diff --git a/src/gui/components/wxGameList.cpp b/src/gui/wxGui/components/wxGameList.cpp similarity index 99% rename from src/gui/components/wxGameList.cpp rename to src/gui/wxGui/components/wxGameList.cpp index 6e761358..20cc49e2 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/wxGui/components/wxGameList.cpp @@ -1,8 +1,8 @@ -#include "gui/components/wxGameList.h" +#include "components/wxGameList.h" -#include "gui/helpers/wxCustomData.h" +#include "helpers/wxCustomData.h" #include "util/helpers/helpers.h" -#include "gui/GameProfileWindow.h" +#include "GameProfileWindow.h" #include @@ -22,9 +22,9 @@ #include "Cafe/TitleList/GameInfo.h" #include "Cafe/TitleList/TitleList.h" -#include "gui/CemuApp.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/MainWindow.h" +#include "CemuApp.h" +#include "helpers/wxHelpers.h" +#include "MainWindow.h" #include "../wxHelper.h" diff --git a/src/gui/components/wxGameList.h b/src/gui/wxGui/components/wxGameList.h similarity index 100% rename from src/gui/components/wxGameList.h rename to src/gui/wxGui/components/wxGameList.h diff --git a/src/gui/components/wxInputDraw.cpp b/src/gui/wxGui/components/wxInputDraw.cpp similarity index 98% rename from src/gui/components/wxInputDraw.cpp rename to src/gui/wxGui/components/wxInputDraw.cpp index e3884b71..65b0ef5b 100644 --- a/src/gui/components/wxInputDraw.cpp +++ b/src/gui/wxGui/components/wxInputDraw.cpp @@ -1,4 +1,4 @@ -#include "gui/components/wxInputDraw.h" +#include "components/wxInputDraw.h" #include diff --git a/src/gui/components/wxInputDraw.h b/src/gui/wxGui/components/wxInputDraw.h similarity index 100% rename from src/gui/components/wxInputDraw.h rename to src/gui/wxGui/components/wxInputDraw.h diff --git a/src/gui/components/wxLogCtrl.cpp b/src/gui/wxGui/components/wxLogCtrl.cpp similarity index 98% rename from src/gui/components/wxLogCtrl.cpp rename to src/gui/wxGui/components/wxLogCtrl.cpp index 732dd26d..f67be425 100644 --- a/src/gui/components/wxLogCtrl.cpp +++ b/src/gui/wxGui/components/wxLogCtrl.cpp @@ -1,4 +1,4 @@ -#include "gui/components/wxLogCtrl.h" +#include "components/wxLogCtrl.h" #include diff --git a/src/gui/components/wxLogCtrl.h b/src/gui/wxGui/components/wxLogCtrl.h similarity index 100% rename from src/gui/components/wxLogCtrl.h rename to src/gui/wxGui/components/wxLogCtrl.h diff --git a/src/gui/components/wxTitleManagerList.cpp b/src/gui/wxGui/components/wxTitleManagerList.cpp similarity index 99% rename from src/gui/components/wxTitleManagerList.cpp rename to src/gui/wxGui/components/wxTitleManagerList.cpp index 7ba8d037..1b3d2143 100644 --- a/src/gui/components/wxTitleManagerList.cpp +++ b/src/gui/wxGui/components/wxTitleManagerList.cpp @@ -1,12 +1,12 @@ -#include "gui/components/wxTitleManagerList.h" -#include "gui/helpers/wxHelpers.h" +#include "components/wxTitleManagerList.h" +#include "helpers/wxHelpers.h" #include "util/helpers/SystemException.h" #include "Cafe/TitleList/GameInfo.h" #include "Cafe/TitleList/TitleInfo.h" #include "Cafe/TitleList/TitleList.h" -#include "gui/components/wxGameList.h" -#include "gui/helpers/wxCustomEvents.h" -#include "gui/helpers/wxHelpers.h" +#include "components/wxGameList.h" +#include "helpers/wxCustomEvents.h" +#include "helpers/wxHelpers.h" #include #include @@ -22,8 +22,8 @@ #include #include "config/ActiveSettings.h" -#include "gui/ChecksumTool.h" -#include "gui/MainWindow.h" +#include "ChecksumTool.h" +#include "MainWindow.h" #include "Cafe/TitleList/TitleId.h" #include "Cafe/TitleList/SaveList.h" #include "Cafe/TitleList/TitleList.h" diff --git a/src/gui/components/wxTitleManagerList.h b/src/gui/wxGui/components/wxTitleManagerList.h similarity index 99% rename from src/gui/components/wxTitleManagerList.h rename to src/gui/wxGui/components/wxTitleManagerList.h index 706de2e7..5de63ec1 100644 --- a/src/gui/components/wxTitleManagerList.h +++ b/src/gui/wxGui/components/wxTitleManagerList.h @@ -1,6 +1,6 @@ #pragma once -#include "gui/helpers/wxCustomData.h" +#include "helpers/wxCustomData.h" #include "config/CemuConfig.h" #include diff --git a/src/gui/debugger/AudioDebuggerWindow.cpp b/src/gui/wxGui/debugger/AudioDebuggerWindow.cpp similarity index 99% rename from src/gui/debugger/AudioDebuggerWindow.cpp rename to src/gui/wxGui/debugger/AudioDebuggerWindow.cpp index d082630e..7463f7dc 100644 --- a/src/gui/debugger/AudioDebuggerWindow.cpp +++ b/src/gui/wxGui/debugger/AudioDebuggerWindow.cpp @@ -1,5 +1,5 @@ -#include "gui/wxgui.h" -#include "gui/debugger/AudioDebuggerWindow.h" +#include "wxgui.h" +#include "debugger/AudioDebuggerWindow.h" #include "Cafe/OS/libs/snd_core/ax.h" #include "Cafe/OS/libs/snd_core/ax_internal.h" diff --git a/src/gui/debugger/AudioDebuggerWindow.h b/src/gui/wxGui/debugger/AudioDebuggerWindow.h similarity index 100% rename from src/gui/debugger/AudioDebuggerWindow.h rename to src/gui/wxGui/debugger/AudioDebuggerWindow.h diff --git a/src/gui/debugger/BreakpointWindow.cpp b/src/gui/wxGui/debugger/BreakpointWindow.cpp similarity index 98% rename from src/gui/debugger/BreakpointWindow.cpp rename to src/gui/wxGui/debugger/BreakpointWindow.cpp index ecb77428..67cd39b5 100644 --- a/src/gui/debugger/BreakpointWindow.cpp +++ b/src/gui/wxGui/debugger/BreakpointWindow.cpp @@ -1,10 +1,10 @@ -#include "gui/wxgui.h" -#include "gui/debugger/BreakpointWindow.h" +#include "wxgui.h" +#include "debugger/BreakpointWindow.h" #include -#include "gui/debugger/DebuggerWindow2.h" -#include "gui/guiWrapper.h" +#include "debugger/DebuggerWindow2.h" +#include "guiWrapper.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cemu/ExpressionParser/ExpressionParser.h" diff --git a/src/gui/debugger/BreakpointWindow.h b/src/gui/wxGui/debugger/BreakpointWindow.h similarity index 92% rename from src/gui/debugger/BreakpointWindow.h rename to src/gui/wxGui/debugger/BreakpointWindow.h index 4851cb52..add75ad6 100644 --- a/src/gui/debugger/BreakpointWindow.h +++ b/src/gui/wxGui/debugger/BreakpointWindow.h @@ -1,5 +1,5 @@ #pragma once -#include "gui/wxcomponents/checkedlistctrl.h" +#include "wxcomponents/checkedlistctrl.h" class DebuggerWindow2; diff --git a/src/gui/debugger/DebuggerWindow2.cpp b/src/gui/wxGui/debugger/DebuggerWindow2.cpp similarity index 98% rename from src/gui/debugger/DebuggerWindow2.cpp rename to src/gui/wxGui/debugger/DebuggerWindow2.cpp index f8c5c931..1e61397c 100644 --- a/src/gui/debugger/DebuggerWindow2.cpp +++ b/src/gui/wxGui/debugger/DebuggerWindow2.cpp @@ -1,5 +1,5 @@ -#include "gui/wxgui.h" -#include "gui/debugger/DebuggerWindow2.h" +#include "wxgui.h" +#include "debugger/DebuggerWindow2.h" #include @@ -7,16 +7,16 @@ #include "Cafe/OS/RPL/rpl_structs.h" #include "Cafe/OS/RPL/rpl_debug_symbols.h" -#include "gui/debugger/RegisterWindow.h" -#include "gui/debugger/DumpWindow.h" +#include "debugger/RegisterWindow.h" +#include "debugger/DumpWindow.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cafe/OS/RPL/rpl.h" -#include "gui/debugger/DisasmCtrl.h" -#include "gui/debugger/SymbolWindow.h" -#include "gui/debugger/BreakpointWindow.h" -#include "gui/debugger/ModuleWindow.h" +#include "debugger/DisasmCtrl.h" +#include "debugger/SymbolWindow.h" +#include "debugger/BreakpointWindow.h" +#include "debugger/ModuleWindow.h" #include "util/helpers/helpers.h" #include "resource/embedded/resources.h" diff --git a/src/gui/debugger/DebuggerWindow2.h b/src/gui/wxGui/debugger/DebuggerWindow2.h similarity index 98% rename from src/gui/debugger/DebuggerWindow2.h rename to src/gui/wxGui/debugger/DebuggerWindow2.h index 0ca44c44..6b452bd2 100644 --- a/src/gui/debugger/DebuggerWindow2.h +++ b/src/gui/wxGui/debugger/DebuggerWindow2.h @@ -1,6 +1,6 @@ #pragma once -#include "gui/debugger/DisasmCtrl.h" +#include "debugger/DisasmCtrl.h" #include "config/XMLConfig.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cafe/OS/RPL/rpl.h" diff --git a/src/gui/debugger/DisasmCtrl.cpp b/src/gui/wxGui/debugger/DisasmCtrl.cpp similarity index 99% rename from src/gui/debugger/DisasmCtrl.cpp rename to src/gui/wxGui/debugger/DisasmCtrl.cpp index dededf2c..15709ecb 100644 --- a/src/gui/debugger/DisasmCtrl.cpp +++ b/src/gui/wxGui/debugger/DisasmCtrl.cpp @@ -1,5 +1,5 @@ -#include "gui/wxgui.h" -#include "gui/debugger/DisasmCtrl.h" +#include "wxgui.h" +#include "debugger/DisasmCtrl.h" #include "Cafe/OS/RPL/rpl_structs.h" #include "Cafe/OS/RPL/rpl.h" @@ -7,9 +7,9 @@ #include "Cafe/OS/RPL/rpl_debug_symbols.h" #include "Cemu/PPCAssembler/ppcAssembler.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "gui/debugger/DebuggerWindow2.h" +#include "debugger/DebuggerWindow2.h" #include "util/helpers/helpers.h" -#include "gui/guiWrapper.h" +#include "guiWrapper.h" #include "Cemu/ExpressionParser/ExpressionParser.h" #include "Cafe/HW/Espresso/Debugger/DebugSymbolStorage.h" diff --git a/src/gui/debugger/DisasmCtrl.h b/src/gui/wxGui/debugger/DisasmCtrl.h similarity index 97% rename from src/gui/debugger/DisasmCtrl.h rename to src/gui/wxGui/debugger/DisasmCtrl.h index 993d5697..39ef09e3 100644 --- a/src/gui/debugger/DisasmCtrl.h +++ b/src/gui/wxGui/debugger/DisasmCtrl.h @@ -1,5 +1,5 @@ #pragma once -#include "gui/components/TextList.h" +#include "components/TextList.h" class DisasmCtrl : public TextList { diff --git a/src/gui/debugger/DumpCtrl.cpp b/src/gui/wxGui/debugger/DumpCtrl.cpp similarity index 99% rename from src/gui/debugger/DumpCtrl.cpp rename to src/gui/wxGui/debugger/DumpCtrl.cpp index cc1d5fee..c40ffaf5 100644 --- a/src/gui/debugger/DumpCtrl.cpp +++ b/src/gui/wxGui/debugger/DumpCtrl.cpp @@ -1,5 +1,5 @@ -#include "gui/wxgui.h" -#include "gui/debugger/DumpCtrl.h" +#include "wxgui.h" +#include "debugger/DumpCtrl.h" #include "Cafe/OS/RPL/rpl.h" #include "Cafe/OS/RPL/rpl_structs.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" diff --git a/src/gui/debugger/DumpCtrl.h b/src/gui/wxGui/debugger/DumpCtrl.h similarity index 95% rename from src/gui/debugger/DumpCtrl.h rename to src/gui/wxGui/debugger/DumpCtrl.h index da5ad4ea..8ccb389d 100644 --- a/src/gui/debugger/DumpCtrl.h +++ b/src/gui/wxGui/debugger/DumpCtrl.h @@ -1,5 +1,5 @@ #pragma once -#include "gui/components/TextList.h" +#include "components/TextList.h" class DumpCtrl : public TextList diff --git a/src/gui/debugger/DumpWindow.cpp b/src/gui/wxGui/debugger/DumpWindow.cpp similarity index 90% rename from src/gui/debugger/DumpWindow.cpp rename to src/gui/wxGui/debugger/DumpWindow.cpp index 35c9d2c8..3053018b 100644 --- a/src/gui/debugger/DumpWindow.cpp +++ b/src/gui/wxGui/debugger/DumpWindow.cpp @@ -1,9 +1,9 @@ -#include "gui/wxgui.h" -#include "gui/debugger/DumpWindow.h" +#include "wxgui.h" +#include "debugger/DumpWindow.h" -#include "gui/debugger/DebuggerWindow2.h" +#include "debugger/DebuggerWindow2.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "gui/debugger/DumpCtrl.h" +#include "debugger/DumpCtrl.h" enum { diff --git a/src/gui/debugger/DumpWindow.h b/src/gui/wxGui/debugger/DumpWindow.h similarity index 90% rename from src/gui/debugger/DumpWindow.h rename to src/gui/wxGui/debugger/DumpWindow.h index 840c5fc6..7837c11e 100644 --- a/src/gui/debugger/DumpWindow.h +++ b/src/gui/wxGui/debugger/DumpWindow.h @@ -1,6 +1,6 @@ #pragma once -#include "gui/debugger/DumpCtrl.h" +#include "debugger/DumpCtrl.h" class DebuggerWindow2; diff --git a/src/gui/debugger/ModuleWindow.cpp b/src/gui/wxGui/debugger/ModuleWindow.cpp similarity index 96% rename from src/gui/debugger/ModuleWindow.cpp rename to src/gui/wxGui/debugger/ModuleWindow.cpp index c24517bd..455e62d7 100644 --- a/src/gui/debugger/ModuleWindow.cpp +++ b/src/gui/wxGui/debugger/ModuleWindow.cpp @@ -1,10 +1,10 @@ -#include "gui/wxgui.h" -#include "gui/guiWrapper.h" -#include "gui/debugger/ModuleWindow.h" +#include "wxgui.h" +#include "guiWrapper.h" +#include "debugger/ModuleWindow.h" #include -#include "gui/debugger/DebuggerWindow2.h" +#include "debugger/DebuggerWindow2.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cafe/OS/RPL/rpl.h" diff --git a/src/gui/debugger/ModuleWindow.h b/src/gui/wxGui/debugger/ModuleWindow.h similarity index 100% rename from src/gui/debugger/ModuleWindow.h rename to src/gui/wxGui/debugger/ModuleWindow.h diff --git a/src/gui/debugger/RegisterCtrl.cpp b/src/gui/wxGui/debugger/RegisterCtrl.cpp similarity index 98% rename from src/gui/debugger/RegisterCtrl.cpp rename to src/gui/wxGui/debugger/RegisterCtrl.cpp index bcf6fb5a..0c12e34b 100644 --- a/src/gui/debugger/RegisterCtrl.cpp +++ b/src/gui/wxGui/debugger/RegisterCtrl.cpp @@ -1,12 +1,12 @@ -#include "gui/wxgui.h" -#include "gui/debugger/RegisterCtrl.h" +#include "wxgui.h" +#include "debugger/RegisterCtrl.h" #include #include "Cafe/OS/RPL/rpl_structs.h" #include "Cafe/OS/RPL/rpl.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" -#include "gui/debugger/DebuggerWindow2.h" +#include "debugger/DebuggerWindow2.h" #define COLOR_DEBUG_ACTIVE_BP 0xFFFFA0FF #define COLOR_DEBUG_ACTIVE 0xFFFFA080 diff --git a/src/gui/debugger/RegisterCtrl.h b/src/gui/wxGui/debugger/RegisterCtrl.h similarity index 93% rename from src/gui/debugger/RegisterCtrl.h rename to src/gui/wxGui/debugger/RegisterCtrl.h index 378a5d5d..46b4cb90 100644 --- a/src/gui/debugger/RegisterCtrl.h +++ b/src/gui/wxGui/debugger/RegisterCtrl.h @@ -1,5 +1,5 @@ #pragma once -#include "gui/components/TextList.h" +#include "components/TextList.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" class RegisterCtrl : public TextList diff --git a/src/gui/debugger/RegisterWindow.cpp b/src/gui/wxGui/debugger/RegisterWindow.cpp similarity index 99% rename from src/gui/debugger/RegisterWindow.cpp rename to src/gui/wxGui/debugger/RegisterWindow.cpp index 69c2273d..5da20b6a 100644 --- a/src/gui/debugger/RegisterWindow.cpp +++ b/src/gui/wxGui/debugger/RegisterWindow.cpp @@ -1,9 +1,9 @@ -#include "gui/wxgui.h" -#include "gui/debugger/RegisterWindow.h" +#include "wxgui.h" +#include "debugger/RegisterWindow.h" #include -#include "gui/debugger/DebuggerWindow2.h" +#include "debugger/DebuggerWindow2.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cafe/OS/RPL/rpl.h" #include "Cafe/OS/RPL/rpl_structs.h" diff --git a/src/gui/debugger/RegisterWindow.h b/src/gui/wxGui/debugger/RegisterWindow.h similarity index 100% rename from src/gui/debugger/RegisterWindow.h rename to src/gui/wxGui/debugger/RegisterWindow.h diff --git a/src/gui/debugger/SymbolCtrl.cpp b/src/gui/wxGui/debugger/SymbolCtrl.cpp similarity index 98% rename from src/gui/debugger/SymbolCtrl.cpp rename to src/gui/wxGui/debugger/SymbolCtrl.cpp index cb1f3b1a..9ca5ec49 100644 --- a/src/gui/debugger/SymbolCtrl.cpp +++ b/src/gui/wxGui/debugger/SymbolCtrl.cpp @@ -1,5 +1,5 @@ -#include "gui/debugger/SymbolCtrl.h" -#include "gui/guiWrapper.h" +#include "debugger/SymbolCtrl.h" +#include "guiWrapper.h" #include "Cafe/OS/RPL/rpl_symbol_storage.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" diff --git a/src/gui/debugger/SymbolCtrl.h b/src/gui/wxGui/debugger/SymbolCtrl.h similarity index 100% rename from src/gui/debugger/SymbolCtrl.h rename to src/gui/wxGui/debugger/SymbolCtrl.h diff --git a/src/gui/debugger/SymbolWindow.cpp b/src/gui/wxGui/debugger/SymbolWindow.cpp similarity index 91% rename from src/gui/debugger/SymbolWindow.cpp rename to src/gui/wxGui/debugger/SymbolWindow.cpp index 79414e96..b1551af5 100644 --- a/src/gui/debugger/SymbolWindow.cpp +++ b/src/gui/wxGui/debugger/SymbolWindow.cpp @@ -1,7 +1,7 @@ -#include "gui/wxgui.h" -#include "gui/guiWrapper.h" -#include "gui/debugger/SymbolWindow.h" -#include "gui/debugger/DebuggerWindow2.h" +#include "wxgui.h" +#include "guiWrapper.h" +#include "debugger/SymbolWindow.h" +#include "debugger/DebuggerWindow2.h" #include "Cafe/HW/Espresso/Debugger/Debugger.h" #include "Cafe/OS/RPL/rpl_symbol_storage.h" diff --git a/src/gui/debugger/SymbolWindow.h b/src/gui/wxGui/debugger/SymbolWindow.h similarity index 92% rename from src/gui/debugger/SymbolWindow.h rename to src/gui/wxGui/debugger/SymbolWindow.h index 2d4cd25f..6cb59576 100644 --- a/src/gui/debugger/SymbolWindow.h +++ b/src/gui/wxGui/debugger/SymbolWindow.h @@ -1,6 +1,6 @@ #pragma once -#include "gui/debugger/SymbolCtrl.h" +#include "debugger/SymbolCtrl.h" class DebuggerWindow2; diff --git a/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp b/src/gui/wxGui/dialogs/CreateAccount/wxCreateAccountDialog.cpp similarity index 98% rename from src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp rename to src/gui/wxGui/dialogs/CreateAccount/wxCreateAccountDialog.cpp index 71b56637..a8a3cf67 100644 --- a/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.cpp +++ b/src/gui/wxGui/dialogs/CreateAccount/wxCreateAccountDialog.cpp @@ -1,4 +1,4 @@ -#include "gui/dialogs/CreateAccount/wxCreateAccountDialog.h" +#include "dialogs/CreateAccount/wxCreateAccountDialog.h" #include "Cafe/Account/Account.h" #include diff --git a/src/gui/dialogs/CreateAccount/wxCreateAccountDialog.h b/src/gui/wxGui/dialogs/CreateAccount/wxCreateAccountDialog.h similarity index 100% rename from src/gui/dialogs/CreateAccount/wxCreateAccountDialog.h rename to src/gui/wxGui/dialogs/CreateAccount/wxCreateAccountDialog.h diff --git a/src/gui/dialogs/SaveImport/SaveImportWindow.cpp b/src/gui/wxGui/dialogs/SaveImport/SaveImportWindow.cpp similarity index 99% rename from src/gui/dialogs/SaveImport/SaveImportWindow.cpp rename to src/gui/wxGui/dialogs/SaveImport/SaveImportWindow.cpp index 2a570bb0..7cdf77b9 100644 --- a/src/gui/dialogs/SaveImport/SaveImportWindow.cpp +++ b/src/gui/wxGui/dialogs/SaveImport/SaveImportWindow.cpp @@ -16,7 +16,7 @@ #include "Cafe/OS/libs/coreinit/coreinit_Time.h" #include "util/helpers/helpers.h" #include "Cafe/HW/Espresso/PPCState.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" SaveImportWindow::SaveImportWindow(wxWindow* parent, uint64 title_id) : wxDialog(parent, wxID_ANY, _("Import save entry"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxFRAME_TOOL_WINDOW | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), diff --git a/src/gui/dialogs/SaveImport/SaveImportWindow.h b/src/gui/wxGui/dialogs/SaveImport/SaveImportWindow.h similarity index 100% rename from src/gui/dialogs/SaveImport/SaveImportWindow.h rename to src/gui/wxGui/dialogs/SaveImport/SaveImportWindow.h diff --git a/src/gui/dialogs/SaveImport/SaveTransfer.cpp b/src/gui/wxGui/dialogs/SaveImport/SaveTransfer.cpp similarity index 99% rename from src/gui/dialogs/SaveImport/SaveTransfer.cpp rename to src/gui/wxGui/dialogs/SaveImport/SaveTransfer.cpp index 14e473a1..f26daa8a 100644 --- a/src/gui/dialogs/SaveImport/SaveTransfer.cpp +++ b/src/gui/wxGui/dialogs/SaveImport/SaveTransfer.cpp @@ -12,7 +12,7 @@ #include "Cafe/Account/Account.h" #include "config/ActiveSettings.h" #include "util/helpers/helpers.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" SaveTransfer::SaveTransfer(wxWindow* parent, uint64 title_id, const wxString& source_account, uint32 source_id) : wxDialog(parent, wxID_ANY, _("Save transfer"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxFRAME_TOOL_WINDOW | wxSYSTEM_MENU | wxTAB_TRAVERSAL | wxCLOSE_BOX), diff --git a/src/gui/dialogs/SaveImport/SaveTransfer.h b/src/gui/wxGui/dialogs/SaveImport/SaveTransfer.h similarity index 100% rename from src/gui/dialogs/SaveImport/SaveTransfer.h rename to src/gui/wxGui/dialogs/SaveImport/SaveTransfer.h diff --git a/src/gui/helpers/wxControlObject.h b/src/gui/wxGui/helpers/wxControlObject.h similarity index 100% rename from src/gui/helpers/wxControlObject.h rename to src/gui/wxGui/helpers/wxControlObject.h diff --git a/src/gui/helpers/wxCustomData.h b/src/gui/wxGui/helpers/wxCustomData.h similarity index 100% rename from src/gui/helpers/wxCustomData.h rename to src/gui/wxGui/helpers/wxCustomData.h diff --git a/src/gui/helpers/wxCustomEvents.cpp b/src/gui/wxGui/helpers/wxCustomEvents.cpp similarity index 87% rename from src/gui/helpers/wxCustomEvents.cpp rename to src/gui/wxGui/helpers/wxCustomEvents.cpp index 081346c5..d91ba9e5 100644 --- a/src/gui/helpers/wxCustomEvents.cpp +++ b/src/gui/wxGui/helpers/wxCustomEvents.cpp @@ -1,4 +1,4 @@ -#include "gui/helpers/wxCustomEvents.h" +#include "helpers/wxCustomEvents.h" wxDEFINE_EVENT(wxEVT_SET_STATUS_BAR_TEXT, wxSetStatusBarTextEvent); wxDEFINE_EVENT(wxEVT_SET_GAUGE_VALUE, wxSetGaugeValue); diff --git a/src/gui/helpers/wxCustomEvents.h b/src/gui/wxGui/helpers/wxCustomEvents.h similarity index 100% rename from src/gui/helpers/wxCustomEvents.h rename to src/gui/wxGui/helpers/wxCustomEvents.h diff --git a/src/gui/helpers/wxHelpers.cpp b/src/gui/wxGui/helpers/wxHelpers.cpp similarity index 95% rename from src/gui/helpers/wxHelpers.cpp rename to src/gui/wxGui/helpers/wxHelpers.cpp index d6b273c1..2dfca215 100644 --- a/src/gui/helpers/wxHelpers.cpp +++ b/src/gui/wxGui/helpers/wxHelpers.cpp @@ -1,11 +1,11 @@ -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" #include #include #include #include -#include "gui/helpers/wxControlObject.h" +#include "helpers/wxControlObject.h" void wxAutosizeColumn(wxListCtrlBase* ctrl, int col) { diff --git a/src/gui/helpers/wxHelpers.h b/src/gui/wxGui/helpers/wxHelpers.h similarity index 100% rename from src/gui/helpers/wxHelpers.h rename to src/gui/wxGui/helpers/wxHelpers.h diff --git a/src/gui/helpers/wxLogEvent.h b/src/gui/wxGui/helpers/wxLogEvent.h similarity index 100% rename from src/gui/helpers/wxLogEvent.h rename to src/gui/wxGui/helpers/wxLogEvent.h diff --git a/src/gui/helpers/wxWayland.cpp b/src/gui/wxGui/helpers/wxWayland.cpp similarity index 95% rename from src/gui/helpers/wxWayland.cpp rename to src/gui/wxGui/helpers/wxWayland.cpp index c677451c..875a0698 100644 --- a/src/gui/helpers/wxWayland.cpp +++ b/src/gui/wxGui/helpers/wxWayland.cpp @@ -1,4 +1,4 @@ -#include "gui/helpers/wxWayland.h" +#include "helpers/wxWayland.h" #if BOOST_OS_LINUX && HAS_WAYLAND diff --git a/src/gui/helpers/wxWayland.h b/src/gui/wxGui/helpers/wxWayland.h similarity index 100% rename from src/gui/helpers/wxWayland.h rename to src/gui/wxGui/helpers/wxWayland.h diff --git a/src/gui/input/InputAPIAddWindow.cpp b/src/gui/wxGui/input/InputAPIAddWindow.cpp similarity index 98% rename from src/gui/input/InputAPIAddWindow.cpp rename to src/gui/wxGui/input/InputAPIAddWindow.cpp index f32a85b6..8b9bea68 100644 --- a/src/gui/input/InputAPIAddWindow.cpp +++ b/src/gui/wxGui/input/InputAPIAddWindow.cpp @@ -1,8 +1,8 @@ -#include "gui/input/InputAPIAddWindow.h" +#include "input/InputAPIAddWindow.h" #include "input/InputManager.h" -#include "gui/helpers/wxCustomData.h" -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxCustomData.h" +#include "helpers/wxHelpers.h" #include "input/api/Controller.h" #include diff --git a/src/gui/input/InputAPIAddWindow.h b/src/gui/wxGui/input/InputAPIAddWindow.h similarity index 97% rename from src/gui/input/InputAPIAddWindow.h rename to src/gui/wxGui/input/InputAPIAddWindow.h index ebee0592..f2ac7a5c 100644 --- a/src/gui/input/InputAPIAddWindow.h +++ b/src/gui/wxGui/input/InputAPIAddWindow.h @@ -6,7 +6,7 @@ #include #include -#include "gui/helpers/wxCustomData.h" +#include "helpers/wxCustomData.h" #include "input/api/Controller.h" class wxComboBox; diff --git a/src/gui/input/InputSettings2.cpp b/src/gui/wxGui/input/InputSettings2.cpp similarity index 98% rename from src/gui/input/InputSettings2.cpp rename to src/gui/wxGui/input/InputSettings2.cpp index bc3d33e0..9d19581a 100644 --- a/src/gui/input/InputSettings2.cpp +++ b/src/gui/wxGui/input/InputSettings2.cpp @@ -1,11 +1,11 @@ -#include "gui/input/InputSettings2.h" +#include "input/InputSettings2.h" #include #include "input/InputManager.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/helpers/wxControlObject.h" -#include "gui/helpers/wxCustomData.h" +#include "helpers/wxHelpers.h" +#include "helpers/wxControlObject.h" +#include "helpers/wxCustomData.h" #include #include @@ -17,16 +17,16 @@ #include #include "config/ActiveSettings.h" -#include "gui/input/InputAPIAddWindow.h" +#include "input/InputAPIAddWindow.h" #include "input/ControllerFactory.h" -#include "gui/input/panels/VPADInputPanel.h" -#include "gui/input/panels/ProControllerInputPanel.h" +#include "input/panels/VPADInputPanel.h" +#include "input/panels/ProControllerInputPanel.h" -#include "gui/input/settings/DefaultControllerSettings.h" -#include "gui/input/panels/ClassicControllerInputPanel.h" -#include "gui/input/panels/WiimoteInputPanel.h" -#include "gui/input/settings/WiimoteControllerSettings.h" +#include "input/settings/DefaultControllerSettings.h" +#include "input/panels/ClassicControllerInputPanel.h" +#include "input/panels/WiimoteInputPanel.h" +#include "input/settings/WiimoteControllerSettings.h" #include "util/EventService.h" #include "resource/embedded/resources.h" diff --git a/src/gui/input/InputSettings2.h b/src/gui/wxGui/input/InputSettings2.h similarity index 100% rename from src/gui/input/InputSettings2.h rename to src/gui/wxGui/input/InputSettings2.h diff --git a/src/gui/input/panels/ClassicControllerInputPanel.cpp b/src/gui/wxGui/input/panels/ClassicControllerInputPanel.cpp similarity index 96% rename from src/gui/input/panels/ClassicControllerInputPanel.cpp rename to src/gui/wxGui/input/panels/ClassicControllerInputPanel.cpp index c0d7c939..ee298e09 100644 --- a/src/gui/input/panels/ClassicControllerInputPanel.cpp +++ b/src/gui/wxGui/input/panels/ClassicControllerInputPanel.cpp @@ -1,4 +1,4 @@ -#include "gui/input/panels/ClassicControllerInputPanel.h" +#include "input/panels/ClassicControllerInputPanel.h" #include #include @@ -6,10 +6,10 @@ #include #include -#include "gui/helpers/wxControlObject.h" +#include "helpers/wxControlObject.h" #include "input/emulated/ClassicController.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/components/wxInputDraw.h" +#include "helpers/wxHelpers.h" +#include "components/wxInputDraw.h" constexpr ClassicController::ButtonId g_kFirstColumnItems[] = { ClassicController::kButtonId_A, ClassicController::kButtonId_B, ClassicController::kButtonId_X, ClassicController::kButtonId_Y, ClassicController::kButtonId_L, ClassicController::kButtonId_R, ClassicController::kButtonId_ZL, ClassicController::kButtonId_ZR, ClassicController::kButtonId_Plus, ClassicController::kButtonId_Minus }; constexpr ClassicController::ButtonId g_kSecondColumnItems[] = { ClassicController::kButtonId_StickL_Up, ClassicController::kButtonId_StickL_Down, ClassicController::kButtonId_StickL_Left, ClassicController::kButtonId_StickL_Right }; diff --git a/src/gui/input/panels/ClassicControllerInputPanel.h b/src/gui/wxGui/input/panels/ClassicControllerInputPanel.h similarity index 90% rename from src/gui/input/panels/ClassicControllerInputPanel.h rename to src/gui/wxGui/input/panels/ClassicControllerInputPanel.h index ff8add27..3ae7a261 100644 --- a/src/gui/input/panels/ClassicControllerInputPanel.h +++ b/src/gui/wxGui/input/panels/ClassicControllerInputPanel.h @@ -2,7 +2,7 @@ #include #include "input/emulated/ClassicController.h" -#include "gui/input/panels/InputPanel.h" +#include "input/panels/InputPanel.h" class wxInputDraw; diff --git a/src/gui/input/panels/InputPanel.cpp b/src/gui/wxGui/input/panels/InputPanel.cpp similarity index 98% rename from src/gui/input/panels/InputPanel.cpp rename to src/gui/wxGui/input/panels/InputPanel.cpp index 6c1b8dda..96aa785e 100644 --- a/src/gui/input/panels/InputPanel.cpp +++ b/src/gui/wxGui/input/panels/InputPanel.cpp @@ -1,9 +1,9 @@ -#include "gui/input/panels/InputPanel.h" +#include "input/panels/InputPanel.h" #include #include -#include "gui/helpers/wxHelpers.h" +#include "helpers/wxHelpers.h" InputPanel::InputPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxNO_BORDER | wxWANTS_CHARS) diff --git a/src/gui/input/panels/InputPanel.h b/src/gui/wxGui/input/panels/InputPanel.h similarity index 98% rename from src/gui/input/panels/InputPanel.h rename to src/gui/wxGui/input/panels/InputPanel.h index 1c30333c..f485144f 100644 --- a/src/gui/input/panels/InputPanel.h +++ b/src/gui/wxGui/input/panels/InputPanel.h @@ -5,7 +5,7 @@ #include "input/emulated/EmulatedController.h" #include "input/api/Controller.h" -#include "gui/wxHelper.h" +#include "wxHelper.h" class ControllerBase; class wxTextCtrl; diff --git a/src/gui/input/panels/ProControllerInputPanel.cpp b/src/gui/wxGui/input/panels/ProControllerInputPanel.cpp similarity index 97% rename from src/gui/input/panels/ProControllerInputPanel.cpp rename to src/gui/wxGui/input/panels/ProControllerInputPanel.cpp index 254a3ab8..c07a2317 100644 --- a/src/gui/input/panels/ProControllerInputPanel.cpp +++ b/src/gui/wxGui/input/panels/ProControllerInputPanel.cpp @@ -1,4 +1,4 @@ -#include "gui/input/panels/ProControllerInputPanel.h" +#include "input/panels/ProControllerInputPanel.h" #include #include @@ -6,8 +6,8 @@ #include #include "input/emulated/ProController.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/components/wxInputDraw.h" +#include "helpers/wxHelpers.h" +#include "components/wxInputDraw.h" const ProController::ButtonId g_kFirstColumnItems[] = { ProController::kButtonId_A, ProController::kButtonId_B, ProController::kButtonId_X, ProController::kButtonId_Y, ProController::kButtonId_L, ProController::kButtonId_R, ProController::kButtonId_ZL, ProController::kButtonId_ZR, ProController::kButtonId_Plus, ProController::kButtonId_Minus }; const ProController::ButtonId g_kSecondColumnItems[] = { ProController::kButtonId_StickL, ProController::kButtonId_StickL_Up, ProController::kButtonId_StickL_Down, ProController::kButtonId_StickL_Left, ProController::kButtonId_StickL_Right }; diff --git a/src/gui/input/panels/ProControllerInputPanel.h b/src/gui/wxGui/input/panels/ProControllerInputPanel.h similarity index 85% rename from src/gui/input/panels/ProControllerInputPanel.h rename to src/gui/wxGui/input/panels/ProControllerInputPanel.h index 7987b44f..6b80e2d7 100644 --- a/src/gui/input/panels/ProControllerInputPanel.h +++ b/src/gui/wxGui/input/panels/ProControllerInputPanel.h @@ -2,8 +2,8 @@ #include #include "input/emulated/ProController.h" -#include "gui/input/panels/InputPanel.h" -#include "gui/components/wxInputDraw.h" +#include "input/panels/InputPanel.h" +#include "components/wxInputDraw.h" class ProControllerInputPanel : public InputPanel { diff --git a/src/gui/input/panels/VPADInputPanel.cpp b/src/gui/wxGui/input/panels/VPADInputPanel.cpp similarity index 97% rename from src/gui/input/panels/VPADInputPanel.cpp rename to src/gui/wxGui/input/panels/VPADInputPanel.cpp index fbcdfde4..2ebaa5b3 100644 --- a/src/gui/input/panels/VPADInputPanel.cpp +++ b/src/gui/wxGui/input/panels/VPADInputPanel.cpp @@ -1,4 +1,4 @@ -#include "gui/input/panels/VPADInputPanel.h" +#include "input/panels/VPADInputPanel.h" #include #include @@ -7,9 +7,9 @@ #include -#include "gui/helpers/wxControlObject.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/components/wxInputDraw.h" +#include "helpers/wxControlObject.h" +#include "helpers/wxHelpers.h" +#include "components/wxInputDraw.h" #include "input/emulated/VPADController.h" constexpr VPADController::ButtonId g_kFirstColumnItems[] = diff --git a/src/gui/input/panels/VPADInputPanel.h b/src/gui/wxGui/input/panels/VPADInputPanel.h similarity index 93% rename from src/gui/input/panels/VPADInputPanel.h rename to src/gui/wxGui/input/panels/VPADInputPanel.h index 3513bbf7..0133ac09 100644 --- a/src/gui/input/panels/VPADInputPanel.h +++ b/src/gui/wxGui/input/panels/VPADInputPanel.h @@ -1,7 +1,7 @@ #pragma once #include -#include "gui/input/panels/InputPanel.h" +#include "input/panels/InputPanel.h" class wxInputDraw; diff --git a/src/gui/input/panels/WiimoteInputPanel.cpp b/src/gui/wxGui/input/panels/WiimoteInputPanel.cpp similarity index 98% rename from src/gui/input/panels/WiimoteInputPanel.cpp rename to src/gui/wxGui/input/panels/WiimoteInputPanel.cpp index fbc4e325..1dffff60 100644 --- a/src/gui/input/panels/WiimoteInputPanel.cpp +++ b/src/gui/wxGui/input/panels/WiimoteInputPanel.cpp @@ -1,4 +1,4 @@ -#include "gui/input/panels/WiimoteInputPanel.h" +#include "input/panels/WiimoteInputPanel.h" #include #include @@ -7,10 +7,10 @@ #include #include -#include "gui/helpers/wxControlObject.h" +#include "helpers/wxControlObject.h" #include "input/emulated/WiimoteController.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/components/wxInputDraw.h" +#include "helpers/wxHelpers.h" +#include "components/wxInputDraw.h" constexpr WiimoteController::ButtonId g_kFirstColumnItems[] = { diff --git a/src/gui/input/panels/WiimoteInputPanel.h b/src/gui/wxGui/input/panels/WiimoteInputPanel.h similarity index 95% rename from src/gui/input/panels/WiimoteInputPanel.h rename to src/gui/wxGui/input/panels/WiimoteInputPanel.h index a7aed99b..cd8f4ba4 100644 --- a/src/gui/input/panels/WiimoteInputPanel.h +++ b/src/gui/wxGui/input/panels/WiimoteInputPanel.h @@ -1,6 +1,6 @@ #pragma once -#include "gui/input/panels/InputPanel.h" +#include "input/panels/InputPanel.h" #include "input/emulated/WiimoteController.h" #include diff --git a/src/gui/input/settings/DefaultControllerSettings.cpp b/src/gui/wxGui/input/settings/DefaultControllerSettings.cpp similarity index 98% rename from src/gui/input/settings/DefaultControllerSettings.cpp rename to src/gui/wxGui/input/settings/DefaultControllerSettings.cpp index 97cf1704..6571513d 100644 --- a/src/gui/input/settings/DefaultControllerSettings.cpp +++ b/src/gui/wxGui/input/settings/DefaultControllerSettings.cpp @@ -1,4 +1,4 @@ -#include "gui/input/settings/DefaultControllerSettings.h" +#include "input/settings/DefaultControllerSettings.h" #include #include @@ -9,10 +9,10 @@ #include #include -#include "gui/helpers/wxControlObject.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/components/wxInputDraw.h" -#include "gui/input/InputAPIAddWindow.h" +#include "helpers/wxControlObject.h" +#include "helpers/wxHelpers.h" +#include "components/wxInputDraw.h" +#include "input/InputAPIAddWindow.h" DefaultControllerSettings::DefaultControllerSettings(wxWindow* parent, const wxPoint& position, std::shared_ptr controller) diff --git a/src/gui/input/settings/DefaultControllerSettings.h b/src/gui/wxGui/input/settings/DefaultControllerSettings.h similarity index 100% rename from src/gui/input/settings/DefaultControllerSettings.h rename to src/gui/wxGui/input/settings/DefaultControllerSettings.h diff --git a/src/gui/input/settings/WiimoteControllerSettings.cpp b/src/gui/wxGui/input/settings/WiimoteControllerSettings.cpp similarity index 98% rename from src/gui/input/settings/WiimoteControllerSettings.cpp rename to src/gui/wxGui/input/settings/WiimoteControllerSettings.cpp index a1ea4ecf..c8e2f82f 100644 --- a/src/gui/input/settings/WiimoteControllerSettings.cpp +++ b/src/gui/wxGui/input/settings/WiimoteControllerSettings.cpp @@ -1,4 +1,4 @@ -#include "gui/input/settings/WiimoteControllerSettings.h" +#include "input/settings/WiimoteControllerSettings.h" #include #include @@ -9,10 +9,10 @@ #include #include -#include "gui/helpers/wxControlObject.h" -#include "gui/helpers/wxHelpers.h" -#include "gui/components/wxInputDraw.h" -#include "gui/input/InputAPIAddWindow.h" +#include "helpers/wxControlObject.h" +#include "helpers/wxHelpers.h" +#include "components/wxInputDraw.h" +#include "input/InputAPIAddWindow.h" #if BOOST_OS_WINDOWS diff --git a/src/gui/input/settings/WiimoteControllerSettings.h b/src/gui/wxGui/input/settings/WiimoteControllerSettings.h similarity index 100% rename from src/gui/input/settings/WiimoteControllerSettings.h rename to src/gui/wxGui/input/settings/WiimoteControllerSettings.h diff --git a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp b/src/gui/wxGui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp similarity index 99% rename from src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp rename to src/gui/wxGui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp index 0c10f325..6963555d 100644 --- a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp +++ b/src/gui/wxGui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.cpp @@ -1,4 +1,4 @@ -#include "gui/wxgui.h" +#include "wxgui.h" #include "Cafe/OS/libs/coreinit/coreinit_Thread.h" #include "Cafe/OS/libs/coreinit/coreinit_Scheduler.h" #include "DebugPPCThreadsWindow.h" diff --git a/src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h b/src/gui/wxGui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h similarity index 100% rename from src/gui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h rename to src/gui/wxGui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h diff --git a/src/gui/windows/TextureRelationViewer/TextureRelationWindow.cpp b/src/gui/wxGui/windows/TextureRelationViewer/TextureRelationWindow.cpp similarity index 99% rename from src/gui/windows/TextureRelationViewer/TextureRelationWindow.cpp rename to src/gui/wxGui/windows/TextureRelationViewer/TextureRelationWindow.cpp index 5b79ffd8..62cb7ec6 100644 --- a/src/gui/windows/TextureRelationViewer/TextureRelationWindow.cpp +++ b/src/gui/wxGui/windows/TextureRelationViewer/TextureRelationWindow.cpp @@ -1,4 +1,4 @@ -#include "gui/wxgui.h" +#include "wxgui.h" #include "TextureRelationWindow.h" #include "Cafe/HW/Latte/Core/LatteTexture.h" diff --git a/src/gui/windows/TextureRelationViewer/TextureRelationWindow.h b/src/gui/wxGui/windows/TextureRelationViewer/TextureRelationWindow.h similarity index 100% rename from src/gui/windows/TextureRelationViewer/TextureRelationWindow.h rename to src/gui/wxGui/windows/TextureRelationViewer/TextureRelationWindow.h diff --git a/src/gui/guiWrapper.cpp b/src/gui/wxGui/wxGuiWrapper.cpp similarity index 85% rename from src/gui/guiWrapper.cpp rename to src/gui/wxGui/wxGuiWrapper.cpp index 1ca86284..0ee139ce 100644 --- a/src/gui/guiWrapper.cpp +++ b/src/gui/wxGui/wxGuiWrapper.cpp @@ -1,24 +1,31 @@ +#include #if BOOST_OS_LINUX #include #include #include +#include #include #ifdef HAS_WAYLAND #include #endif #endif -#include "gui/wxgui.h" -#include "gui/guiWrapper.h" -#include "gui/CemuApp.h" -#include "gui/MainWindow.h" -#include "gui/debugger/DebuggerWindow2.h" +#if BOOST_OS_MACOS +#include +#endif + +#include "wxgui.h" +#include "guiWrapper.h" +#include "CemuApp.h" +#include "MainWindow.h" +#include "debugger/DebuggerWindow2.h" #include "Cafe/HW/Latte/Core/Latte.h" #include "config/ActiveSettings.h" #include "config/NetworkSettings.h" #include "config/CemuConfig.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/CafeSystem.h" +#include "LoggingWindow.h" #include "wxHelper.h" #include @@ -307,9 +314,53 @@ bool gui_isKeyDown(uint32 key) bool gui_isKeyDown(PlatformKeyCodes key) { - return gui_isKeyDown((std::underlying_type_t)key); + uint32 keyCode = 0; + +#if BOOST_OS_WINDOWS + switch (key) + { + case PlatformKeyCodes::LCONTROL: + keyCode = VK_LCONTROL; + break; + case PlatformKeyCodes::RCONTROL: + keyCode = VK_RCONTROL; + break; + case PlatformKeyCodes::TAB: + keyCode = VK_TAB; + break; + } +#elif BOOST_OS_LINUX + switch (key) + { + case PlatformKeyCodes::LCONTROL: + keyCode = GDK_KEY_Control_L; + break; + case PlatformKeyCodes::RCONTROL: + keyCode = GDK_KEY_Control_R; + break; + case PlatformKeyCodes::TAB: + keyCode = GDK_KEY_Tab; + break; + } +#elif BOOST_OS_MACOS + switch (key) + { + case PlatformKeyCodes::LCONTROL: + keyCode = kVK_Control; + break; + case PlatformKeyCodes::RCONTROL: + keyCode = kVK_RightControl; + break; + case PlatformKeyCodes::TAB: + keyCode = kVK_Tab; + break; + } +#endif + + return gui_isKeyDown(keyCode); } + void gui_notifyGameLoaded() { std::shared_lock lock(g_mutex); @@ -395,3 +446,19 @@ void debuggerWindow_notifyModuleUnloaded(void* module) wxQueueEvent(g_debugger_window, evt); } } +void gui_requestGameListRefresh() +{ + MainWindow::RequestGameListRefresh(); +} + +void gui_loggingWindowLog(std::string_view filter, std::string_view message) +{ + LoggingWindow::Log(filter, message); +} + +void gui_loggingWindowLog(std::string_view message) +{ + LoggingWindow::Log(message); +} + + diff --git a/src/gui/wxHelper.h b/src/gui/wxGui/wxHelper.h similarity index 100% rename from src/gui/wxHelper.h rename to src/gui/wxGui/wxHelper.h diff --git a/src/gui/wxcomponents/checked.xpm b/src/gui/wxGui/wxcomponents/checked.xpm similarity index 100% rename from src/gui/wxcomponents/checked.xpm rename to src/gui/wxGui/wxcomponents/checked.xpm diff --git a/src/gui/wxcomponents/checked2.xpm b/src/gui/wxGui/wxcomponents/checked2.xpm similarity index 100% rename from src/gui/wxcomponents/checked2.xpm rename to src/gui/wxGui/wxcomponents/checked2.xpm diff --git a/src/gui/wxcomponents/checked_d.xpm b/src/gui/wxGui/wxcomponents/checked_d.xpm similarity index 100% rename from src/gui/wxcomponents/checked_d.xpm rename to src/gui/wxGui/wxcomponents/checked_d.xpm diff --git a/src/gui/wxcomponents/checked_dis.xpm b/src/gui/wxGui/wxcomponents/checked_dis.xpm similarity index 100% rename from src/gui/wxcomponents/checked_dis.xpm rename to src/gui/wxGui/wxcomponents/checked_dis.xpm diff --git a/src/gui/wxcomponents/checked_ld.xpm b/src/gui/wxGui/wxcomponents/checked_ld.xpm similarity index 100% rename from src/gui/wxcomponents/checked_ld.xpm rename to src/gui/wxGui/wxcomponents/checked_ld.xpm diff --git a/src/gui/wxcomponents/checked_mo.xpm b/src/gui/wxGui/wxcomponents/checked_mo.xpm similarity index 100% rename from src/gui/wxcomponents/checked_mo.xpm rename to src/gui/wxGui/wxcomponents/checked_mo.xpm diff --git a/src/gui/wxcomponents/checkedlistctrl.cpp b/src/gui/wxGui/wxcomponents/checkedlistctrl.cpp similarity index 98% rename from src/gui/wxcomponents/checkedlistctrl.cpp rename to src/gui/wxGui/wxcomponents/checkedlistctrl.cpp index 9728f1c6..32954549 100644 --- a/src/gui/wxcomponents/checkedlistctrl.cpp +++ b/src/gui/wxGui/wxcomponents/checkedlistctrl.cpp @@ -18,7 +18,7 @@ #endif // includes -#include "gui/wxcomponents/checkedlistctrl.h" +#include "wxcomponents/checkedlistctrl.h" #include #include @@ -27,10 +27,10 @@ // resources -#include "gui/wxcomponents/checked.xpm" -#include "gui/wxcomponents/unchecked.xpm" -#include "gui/wxcomponents/checked_dis.xpm" -#include "gui/wxcomponents/unchecked_dis.xpm" +#include "wxcomponents/checked.xpm" +#include "wxcomponents/unchecked.xpm" +#include "wxcomponents/checked_dis.xpm" +#include "wxcomponents/unchecked_dis.xpm" IMPLEMENT_CLASS(wxCheckedListCtrl, wxListCtrl) BEGIN_EVENT_TABLE(wxCheckedListCtrl, wxListCtrl) diff --git a/src/gui/wxcomponents/checkedlistctrl.h b/src/gui/wxGui/wxcomponents/checkedlistctrl.h similarity index 100% rename from src/gui/wxcomponents/checkedlistctrl.h rename to src/gui/wxGui/wxcomponents/checkedlistctrl.h diff --git a/src/gui/wxcomponents/checktree.cpp b/src/gui/wxGui/wxcomponents/checktree.cpp similarity index 97% rename from src/gui/wxcomponents/checktree.cpp rename to src/gui/wxGui/wxcomponents/checktree.cpp index 6f8ca557..723b7a9e 100644 --- a/src/gui/wxcomponents/checktree.cpp +++ b/src/gui/wxGui/wxcomponents/checktree.cpp @@ -1,12 +1,12 @@ -#include "gui/wxcomponents/checktree.h" -#include "gui/wxcomponents/checked2.xpm" -#include "gui/wxcomponents/checked_d.xpm" -#include "gui/wxcomponents/checked_ld.xpm" -#include "gui/wxcomponents/checked_mo.xpm" -#include "gui/wxcomponents/unchecked2.xpm" -#include "gui/wxcomponents/unchecked_d.xpm" -#include "gui/wxcomponents/unchecked_ld.xpm" -#include "gui/wxcomponents/unchecked_mo.xpm" +#include "wxcomponents/checktree.h" +#include "wxcomponents/checked2.xpm" +#include "wxcomponents/checked_d.xpm" +#include "wxcomponents/checked_ld.xpm" +#include "wxcomponents/checked_mo.xpm" +#include "wxcomponents/unchecked2.xpm" +#include "wxcomponents/unchecked_d.xpm" +#include "wxcomponents/unchecked_ld.xpm" +#include "wxcomponents/unchecked_mo.xpm" #include #include diff --git a/src/gui/wxcomponents/checktree.h b/src/gui/wxGui/wxcomponents/checktree.h similarity index 100% rename from src/gui/wxcomponents/checktree.h rename to src/gui/wxGui/wxcomponents/checktree.h diff --git a/src/gui/wxcomponents/unchecked.xpm b/src/gui/wxGui/wxcomponents/unchecked.xpm similarity index 100% rename from src/gui/wxcomponents/unchecked.xpm rename to src/gui/wxGui/wxcomponents/unchecked.xpm diff --git a/src/gui/wxcomponents/unchecked2.xpm b/src/gui/wxGui/wxcomponents/unchecked2.xpm similarity index 100% rename from src/gui/wxcomponents/unchecked2.xpm rename to src/gui/wxGui/wxcomponents/unchecked2.xpm diff --git a/src/gui/wxcomponents/unchecked_d.xpm b/src/gui/wxGui/wxcomponents/unchecked_d.xpm similarity index 100% rename from src/gui/wxcomponents/unchecked_d.xpm rename to src/gui/wxGui/wxcomponents/unchecked_d.xpm diff --git a/src/gui/wxcomponents/unchecked_dis.xpm b/src/gui/wxGui/wxcomponents/unchecked_dis.xpm similarity index 100% rename from src/gui/wxcomponents/unchecked_dis.xpm rename to src/gui/wxGui/wxcomponents/unchecked_dis.xpm diff --git a/src/gui/wxcomponents/unchecked_ld.xpm b/src/gui/wxGui/wxcomponents/unchecked_ld.xpm similarity index 100% rename from src/gui/wxcomponents/unchecked_ld.xpm rename to src/gui/wxGui/wxcomponents/unchecked_ld.xpm diff --git a/src/gui/wxcomponents/unchecked_mo.xpm b/src/gui/wxGui/wxcomponents/unchecked_mo.xpm similarity index 100% rename from src/gui/wxcomponents/unchecked_mo.xpm rename to src/gui/wxGui/wxcomponents/unchecked_mo.xpm diff --git a/src/gui/wxgui.h b/src/gui/wxGui/wxgui.h similarity index 98% rename from src/gui/wxgui.h rename to src/gui/wxGui/wxgui.h index 098449d6..b2f5fdb2 100644 --- a/src/gui/wxgui.h +++ b/src/gui/wxGui/wxgui.h @@ -34,8 +34,6 @@ #include #include -extern bool g_inputConfigWindowHasFocus; - // wx helper functions #include struct wxStringFormatParameters From 6aed5dde4655f81acecf24bb29214f8962577946 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Thu, 8 Jun 2023 08:51:15 +0300 Subject: [PATCH 07/30] Make SDL & OpenGL optional and fix build on Android --- CMakeLists.txt | 24 +++-- cmake/vcpkg_android.cmake | 99 +++++++++++++++++++ dependencies/ih264d/decoder/ih264d_dpb_mgr.c | 7 -- src/CMakeLists.txt | 12 ++- .../HW/Latte/LatteAddrLib/LatteAddrLib.cpp | 8 +- .../Latte/Renderer/OpenGL/OpenGLRenderer.cpp | 5 + src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h | 8 +- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 41 +++++++- .../HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 10 +- src/Common/CMakeLists.txt | 2 +- .../ExceptionHandler_posix.cpp | 2 + src/Common/GLInclude/GLInclude.h | 12 ++- src/Common/betype.h | 12 +-- src/Common/platform.h | 3 + src/Common/precompiled.h | 9 +- src/asm/CMakeLists.txt | 2 + src/config/ActiveSettings.h | 4 + src/gui/guiWrapper.h | 2 +- src/imgui/imgui_impl_opengl3.cpp | 5 +- src/input/CMakeLists.txt | 18 +++- src/input/ControllerFactory.cpp | 2 + src/input/api/SDL/SDLControllerProvider.h | 6 +- src/input/emulated/ClassicController.cpp | 4 + src/input/emulated/ProController.cpp | 4 + src/input/emulated/VPADController.cpp | 4 + src/main.cpp | 4 +- src/util/CMakeLists.txt | 10 +- src/util/Fiber/FiberBoost.cpp | 83 ++++++++++++++++ vcpkg.json | 12 ++- 29 files changed, 360 insertions(+), 54 deletions(-) create mode 100644 cmake/vcpkg_android.cmake create mode 100644 src/util/Fiber/FiberBoost.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 26d676bc..bb40f2ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,13 +17,20 @@ if (EXPERIMENTAL_VERSION) endif() if (ENABLE_VCPKG) - if(UNIX AND NOT APPLE) + + if(UNIX AND NOT APPLE AND NOT VCPKG_TARGET_ANDROID) set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports_linux") else() set(VCPKG_OVERLAY_PORTS "${CMAKE_CURRENT_LIST_DIR}/dependencies/vcpkg_overlay_ports") endif() - set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/vcpkg/scripts/buildsystems/vcpkg.cmake" - CACHE STRING "Vcpkg toolchain file") + if(VCPKG_TARGET_ANDROID) + set(ENV{ANDROID_NDK_HOME} ${ANDROID_NDK}) + set(ENV{VCPKG_ROOT} "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/vcpkg") + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/vcpkg_android.cmake") + else() + set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/vcpkg/scripts/buildsystems/vcpkg.cmake" + CACHE STRING "Vcpkg toolchain file") + endif() # Set this so that all the various find_package() calls don't need an explicit # CONFIG option set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) @@ -103,11 +110,14 @@ option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON) set(THREADS_PREFER_PTHREAD_FLAG true) find_package(Threads REQUIRED) -find_package(SDL2 REQUIRED) +if(ENABLE_SDL) + find_package(SDL2 REQUIRED) + add_compile_definitions("HAS_SDL=1") +endif() find_package(CURL REQUIRED) find_package(pugixml REQUIRED) find_package(RapidJSON REQUIRED) -find_package(Boost COMPONENTS program_options filesystem nowide REQUIRED) +find_package(Boost COMPONENTS program_options filesystem nowide context REQUIRED) find_package(libzip REQUIRED) find_package(glslang REQUIRED) find_package(ZLIB REQUIRED) @@ -122,7 +132,7 @@ if (NOT TARGET glslang::SPIRV AND TARGET SPIRV) add_library(glslang::SPIRV ALIAS SPIRV) endif() -if (UNIX AND NOT APPLE) +if (UNIX AND NOT APPLE AND NOT ANDROID) find_package(X11 REQUIRED) if (ENABLE_WAYLAND) find_package(Wayland REQUIRED Client) @@ -155,7 +165,7 @@ if (ENABLE_DISCORD_RPC) target_include_directories(discord-rpc INTERFACE ./dependencies/discord-rpc/include) endif() -if(UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE AND NOT ANDROID) if(ENABLE_FERAL_GAMEMODE) add_compile_definitions(ENABLE_FERAL_GAMEMODE) add_subdirectory(dependencies/gamemode EXCLUDE_FROM_ALL) diff --git a/cmake/vcpkg_android.cmake b/cmake/vcpkg_android.cmake new file mode 100644 index 00000000..3f09b111 --- /dev/null +++ b/cmake/vcpkg_android.cmake @@ -0,0 +1,99 @@ +# +# vcpkg_android.cmake +# +# Helper script when using vcpkg with cmake. It should be triggered via the variable VCPKG_TARGET_ANDROID +# +# For example: +# if (VCPKG_TARGET_ANDROID) +# include("cmake/vcpkg_android.cmake") +# endif() +# +# This script will: +# 1 & 2. check the presence of needed env variables: ANDROID_NDK_HOME and VCPKG_ROOT +# 3. set VCPKG_TARGET_TRIPLET according to ANDROID_ABI +# 4. Combine vcpkg and Android toolchains by setting CMAKE_TOOLCHAIN_FILE +# and VCPKG_CHAINLOAD_TOOLCHAIN_FILE + +# Note: VCPKG_TARGET_ANDROID is not an official Vcpkg variable. +# it is introduced for the need of this script + +if (VCPKG_TARGET_ANDROID) + + # + # 1. Check the presence of environment variable ANDROID_NDK_HOME + # + if (NOT DEFINED ENV{ANDROID_NDK_HOME}) + message(FATAL_ERROR " + Please set an environment variable ANDROID_NDK_HOME + For example: + export ANDROID_NDK_HOME=/home/your-account/Android/Sdk/ndk-bundle + Or: + export ANDROID_NDK_HOME=/home/your-account/Android/android-ndk-r21b + ") + endif() + + # + # 2. Check the presence of environment variable VCPKG_ROOT + # + if (NOT DEFINED ENV{VCPKG_ROOT}) + message(FATAL_ERROR " + Please set an environment variable VCPKG_ROOT + For example: + export VCPKG_ROOT=/path/to/vcpkg + ") + endif() + + + # + # 3. Set VCPKG_TARGET_TRIPLET according to ANDROID_ABI + # + # There are four different Android ABI, each of which maps to + # a vcpkg triplet. The following table outlines the mapping from vcpkg architectures to android architectures + # + # |VCPKG_TARGET_TRIPLET | ANDROID_ABI | + # |---------------------------|----------------------| + # |arm64-android | arm64-v8a | + # |arm-android | armeabi-v7a | + # |x64-android | x86_64 | + # |x86-android | x86 | + # + # The variable must be stored in the cache in order to successfully the two toolchains. + # + if (ANDROID_ABI MATCHES "arm64-v8a") + set(VCPKG_TARGET_TRIPLET "arm64-android" CACHE STRING "" FORCE) + elseif(ANDROID_ABI MATCHES "armeabi-v7a") + set(VCPKG_TARGET_TRIPLET "arm-android" CACHE STRING "" FORCE) + elseif(ANDROID_ABI MATCHES "x86_64") + set(VCPKG_TARGET_TRIPLET "x64-android" CACHE STRING "" FORCE) + elseif(ANDROID_ABI MATCHES "x86") + set(VCPKG_TARGET_TRIPLET "x86-android" CACHE STRING "" FORCE) + else() + message(FATAL_ERROR " + Please specify ANDROID_ABI + For example + cmake ... -DANDROID_ABI=armeabi-v7a + + Possible ABIs are: arm64-v8a, armeabi-v7a, x64-android, x86-android + ") + endif() + message("vcpkg_android.cmake: VCPKG_TARGET_TRIPLET was set to ${VCPKG_TARGET_TRIPLET}") + + + # + # 4. Combine vcpkg and Android toolchains + # + + # vcpkg and android both provide dedicated toolchains: + # + # vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake + # android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake + # + # When using vcpkg, the vcpkg toolchain shall be specified first. + # However, vcpkg provides a way to preload and additional toolchain, + # with the VCPKG_CHAINLOAD_TOOLCHAIN_FILE option. + set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE $ENV{ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake) + set(CMAKE_TOOLCHAIN_FILE $ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake) + message("vcpkg_android.cmake: CMAKE_TOOLCHAIN_FILE was set to ${CMAKE_TOOLCHAIN_FILE}") + message("vcpkg_android.cmake: VCPKG_CHAINLOAD_TOOLCHAIN_FILE was set to ${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}") + +endif(VCPKG_TARGET_ANDROID) diff --git a/dependencies/ih264d/decoder/ih264d_dpb_mgr.c b/dependencies/ih264d/decoder/ih264d_dpb_mgr.c index ce977d73..0e36cd32 100644 --- a/dependencies/ih264d/decoder/ih264d_dpb_mgr.c +++ b/dependencies/ih264d/decoder/ih264d_dpb_mgr.c @@ -17,9 +17,6 @@ ***************************************************************************** * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore */ -#ifdef __ANDROID__ -#include -#endif #include "ih264_typedefs.h" #include "ih264_macros.h" #include "ih264_platform_macros.h" @@ -902,10 +899,6 @@ WORD32 ih264d_read_mmco_commands(struct _DecStruct * ps_dec) { if (j >= MAX_REF_BUFS) { -#ifdef __ANDROID__ - ALOGE("b/25818142"); - android_errorWriteLog(0x534e4554, "25818142"); -#endif ps_dpb_cmds->u1_num_of_commands = 0; return -1; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a343f5a3..308ceafc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,8 @@ elseif(UNIX) VK_USE_PLATFORM_MACOS_MVK VK_USE_PLATFORM_METAL_EXT ) + elseif(ANDROID) + add_compile_definitions(VK_USE_PLATFORM_ANDROID_KHR) else() add_compile_definitions( VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces? @@ -115,10 +117,16 @@ target_link_libraries(CemuBin PRIVATE CemuGui CemuInput CemuUtil - OpenGL::GL - SDL2::SDL2 ) +if(ENABLE_OPENGL) + target_link_libraries(CemuBin PRIVATE OpenGL::GL) +endif() + +if(ENABLE_SDL) + target_link_libraries(CemuBin PRIVATE SDL2::SDL2) +endif() + if(UNIX AND NOT APPLE) # due to nasm output some linkers will make stack executable # cemu does not require this so we explicity disable it diff --git a/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp b/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp index 60a6e2c4..c111e5af 100644 --- a/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp +++ b/src/Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.cpp @@ -2,7 +2,9 @@ #include "Cafe/HW/Latte/LatteAddrLib/LatteAddrLib.h" #include "Cafe/OS/libs/gx2/GX2_Surface.h" #include - +#if __ANDROID__ +#include +#endif /* Info: @@ -73,7 +75,11 @@ namespace LatteAddrLib uint32 NextPow2(uint32 dim) { +#if __ANDROID__ + return boost::core::bit_ceil(dim); +#else return std::bit_ceil(dim); +#endif } uint32 GetBitsPerPixel(E_HWSURFFMT format, uint32* pElemMode, uint32* pExpandX, uint32* pExpandY) diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp index 7a92f9f9..fdeda42c 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp @@ -209,6 +209,11 @@ void LoadOpenGLImports() #include "Common/GLInclude/glFunctions.h" #undef GLFUNC } +#elif __ANDROID__ +void LoadOpenGLImports() +{ + cemu_assert_unimplemented(); +} #elif BOOST_OS_LINUX GL_IMPORT _GetOpenGLFunction(void* hLib, PFNGLXGETPROCADDRESSPROC func, const char* name) { diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h index de4f1bb8..b50c5a74 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h @@ -127,12 +127,16 @@ VKFUNC_DEVICE(vkCmdBindPipeline); // swapchain #if BOOST_OS_LINUX +#if __ANDROID__ +VKFUNC_INSTANCE(vkCreateAndroidSurfaceKHR); +#else VKFUNC_INSTANCE(vkCreateXlibSurfaceKHR); VKFUNC_INSTANCE(vkCreateXcbSurfaceKHR); #ifdef HAS_WAYLAND VKFUNC_INSTANCE(vkCreateWaylandSurfaceKHR); -#endif -#endif +#endif // HAS_WAYLAND +#endif // __ANDROID__ +#endif // BOOST_OS_LINUX #if BOOST_OS_WINDOWS VKFUNC_INSTANCE(vkCreateWin32SurfaceKHR); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index f7119b75..166ac016 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -106,15 +106,19 @@ std::vector VulkanRenderer::GetDevices() requiredExtensions.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); #if BOOST_OS_WINDOWS requiredExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); - #elif BOOST_OS_LINUX + #elif BOOST_OS_LINUX + #if __ANDROID__ + requiredExtensions.emplace_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); + #else auto backend = gui_getWindowInfo().window_main.backend; if(backend == WindowHandleInfo::Backend::X11) requiredExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); #ifdef HAS_WAYLAND else if (backend == WindowHandleInfo::Backend::WAYLAND) requiredExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); - #endif - #elif BOOST_OS_MACOS + #endif // HAS_WAYLAND + #endif // __ANDROID__ + #elif BOOST_OS_MACOS requiredExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); #endif @@ -1177,14 +1181,18 @@ std::vector VulkanRenderer::CheckInstanceExtensionSupport(FeatureCo requiredInstanceExtensions.emplace_back(VK_KHR_SURFACE_EXTENSION_NAME); #if BOOST_OS_WINDOWS requiredInstanceExtensions.emplace_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); - #elif BOOST_OS_LINUX + #elif BOOST_OS_LINUX + #if __ANDROID__ + requiredInstanceExtensions.emplace_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME); + #else auto backend = gui_getWindowInfo().window_main.backend; if(backend == WindowHandleInfo::Backend::X11) requiredInstanceExtensions.emplace_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); #if HAS_WAYLAND else if (backend == WindowHandleInfo::Backend::WAYLAND) requiredInstanceExtensions.emplace_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); - #endif + #endif // HAS_WAYLAND + #endif // __ANDROID__ #elif BOOST_OS_MACOS requiredInstanceExtensions.emplace_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME); #endif @@ -1265,6 +1273,25 @@ VkSurfaceKHR VulkanRenderer::CreateWinSurface(VkInstance instance, HWND hwindow) #endif #if BOOST_OS_LINUX +#if __ANDROID__ +VkSurfaceKHR VulkanRenderer::CreateAndroidSurface(VkInstance instance, ANativeWindow* window) +{ + VkAndroidSurfaceCreateInfoKHR sci{}; + sci.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + sci.flags = 0; + sci.window = window; + + VkSurfaceKHR result; + VkResult err; + if ((err = vkCreateAndroidSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS) + { + forceLog_printf("Cannot create an Android Vulkan surface: %d", (sint32)err); + throw std::runtime_error(fmt::format("Cannot create an Android Vulkan surface: {}", err)); + } + + return result; +} +#else VkSurfaceKHR VulkanRenderer::CreateXlibSurface(VkInstance instance, Display* dpy, Window window) { VkXlibSurfaceCreateInfoKHR sci{}; @@ -1322,6 +1349,7 @@ VkSurfaceKHR VulkanRenderer::CreateWaylandSurface(VkInstance instance, wl_displa return result; } #endif // HAS_WAYLAND +#endif // __ANDROID__ #endif // BOOST_OS_LINUX VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo) @@ -1329,6 +1357,8 @@ VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struc #if BOOST_OS_WINDOWS return CreateWinSurface(instance, windowInfo.hwnd); #elif BOOST_OS_LINUX +#if __ANDROID__ +#else if(windowInfo.backend == WindowHandleInfo::Backend::X11) return CreateXlibSurface(instance, windowInfo.xlib_display, windowInfo.xlib_window); #ifdef HAS_WAYLAND @@ -1336,6 +1366,7 @@ VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struc return CreateWaylandSurface(instance, windowInfo.display, windowInfo.surface); #endif return {}; +#endif // __ANDROID__ #elif BOOST_OS_MACOS return CreateCocoaSurface(instance, windowInfo.handle); #endif diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 147b6c15..93e582e7 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -199,12 +199,16 @@ public: static VkSurfaceKHR CreateWinSurface(VkInstance instance, HWND hwindow); #endif #if BOOST_OS_LINUX +#if __ANDROID__ + static VkSurfaceKHR CreateAndroidSurface(VkInstance instance, ANativeWindow* window); +#else static VkSurfaceKHR CreateXlibSurface(VkInstance instance, Display* dpy, Window window); static VkSurfaceKHR CreateXcbSurface(VkInstance instance, xcb_connection_t* connection, xcb_window_t window); - #ifdef HAS_WAYLAND +#ifdef HAS_WAYLAND static VkSurfaceKHR CreateWaylandSurface(VkInstance instance, wl_display* display, wl_surface* surface); - #endif -#endif +#endif // HAS_WAYLAND +#endif // __ANDROID__ +#endif // BOOST_OS_LINUX static VkSurfaceKHR CreateFramebufferSurface(VkInstance instance, struct WindowHandleInfo& windowInfo); diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index 7ed3d67a..4442501b 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -66,7 +66,7 @@ target_link_libraries(CemuCommon PRIVATE glm::glm ) -if (UNIX AND NOT APPLE) +if (UNIX AND NOT APPLE AND NOT ANDROID) target_link_libraries(CemuCommon PRIVATE X11::X11 X11::Xrender X11::Xutil) endif() diff --git a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp index 34430e37..61809680 100644 --- a/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp +++ b/src/Common/ExceptionHandler/ExceptionHandler_posix.cpp @@ -61,6 +61,7 @@ void DemangleAndPrintBacktrace(char** backtrace, size_t size) // handle signals that would dump core, print stacktrace and then dump depending on config void handlerDumpingSignal(int sig, siginfo_t *info, void *context) { +#if !__ANDROID__ if(!CrashLog_Create()) return; // give up if crashlog was already created @@ -118,6 +119,7 @@ void handlerDumpingSignal(int sig, siginfo_t *info, void *context) raise(sig); return; } +#endif // __ANDROID__ // exit process ignoring all issues _Exit(1); } diff --git a/src/Common/GLInclude/GLInclude.h b/src/Common/GLInclude/GLInclude.h index 6f5d33db..b68a7a7e 100644 --- a/src/Common/GLInclude/GLInclude.h +++ b/src/Common/GLInclude/GLInclude.h @@ -6,8 +6,12 @@ #include "wglext.h" #endif -#if BOOST_OS_LINUX > 0 - +#if BOOST_OS_LINUX +#if __ANDROID__ +#define EGL_EGL_PROTOTYPES 0 +#include "egl.h" +#undef EGL_EGL_PROTOTYPES +#else // from Xlib #define Bool int #define Status int @@ -33,8 +37,8 @@ typedef struct __GLXFBConfigRec *GLXFBConfig; #undef Status #undef True #undef False - -#endif +#endif // __ANDROID__ +#endif // BOOST_OS_LINUX #define GLFUNC(__type, __name) extern __type __name; #define EGLFUNC(__type, __name) extern __type __name; diff --git a/src/Common/betype.h b/src/Common/betype.h index e684fb93..da5ff045 100644 --- a/src/Common/betype.h +++ b/src/Common/betype.h @@ -165,36 +165,36 @@ public: return tmp; } - betype& operator^=(const betype& v) requires std::integral + betype& operator^=(const betype& v) requires std::is_integral_v { m_value ^= v.m_value; return *this; } - betype& operator>>=(std::size_t idx) requires std::integral + betype& operator>>=(std::size_t idx) requires std::is_integral_v { m_value = SwapEndian(T(value() >> idx)); return *this; } - betype& operator<<=(std::size_t idx) requires std::integral + betype& operator<<=(std::size_t idx) requires std::is_integral_v { m_value = SwapEndian(T(value() << idx)); return *this; } - betype operator~() const requires std::integral + betype operator~() const requires std::is_integral_v { return from_bevalue(T(~m_value)); } - betype& operator++() requires std::integral + betype& operator++() requires std::is_integral_v { m_value = SwapEndian(T(value() + 1)); return *this; } - betype& operator--() requires std::integral + betype& operator--() requires std::is_integral_v { m_value = SwapEndian(T(value() - 1)); return *this; diff --git a/src/Common/platform.h b/src/Common/platform.h index 3f480f43..25006ecb 100644 --- a/src/Common/platform.h +++ b/src/Common/platform.h @@ -5,6 +5,9 @@ #if BOOST_OS_WINDOWS #include "Common/windows/platform.h" +#elif __ANDROID__ +#include +#include "Common/unix/platform.h" #elif BOOST_OS_LINUX #include #include diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 939e3ec4..efa29487 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -74,7 +74,6 @@ #include #include #include -#include #include #include @@ -450,16 +449,24 @@ inline std::string_view _utf8Wrapper(std::u8string_view input) // convert fs::path to utf8 encoded string inline std::string _pathToUtf8(const fs::path& path) { +#if __ANDROID__ + return path.generic_string(); +#else std::u8string strU8 = path.generic_u8string(); std::string v((const char*)strU8.data(), strU8.size()); return v; +#endif // __ANDROID__ } // convert utf8 encoded string to fs::path inline fs::path _utf8ToPath(std::string_view input) { +#if __ANDROID__ + return fs::path(input); +#else std::basic_string_view v((char8_t*)input.data(), input.size()); return fs::path(v); +#endif // __ANDROID__ } class RunAtCemuBoot // -> replaces this with direct function calls. Linkers other than MSVC may optimize way object files entirely if they are not referenced from outside. So a source file self-registering using this would be causing issues diff --git a/src/asm/CMakeLists.txt b/src/asm/CMakeLists.txt index 5d9f84c2..fa9f74a9 100644 --- a/src/asm/CMakeLists.txt +++ b/src/asm/CMakeLists.txt @@ -16,6 +16,8 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") set_property(TARGET CemuAsm PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + elseif(ANDROID) + set(CMAKE_ASM_NASM_COMPILE_OBJECT " -g dwarf2 -f elf64 -o ") else() # NASM diff --git a/src/config/ActiveSettings.h b/src/config/ActiveSettings.h index 678c3966..4ca6caee 100644 --- a/src/config/ActiveSettings.h +++ b/src/config/ActiveSettings.h @@ -24,8 +24,12 @@ private: } static fs::path GetPath(const fs::path& path, std::string_view p) { +#if __ANDROID__ + return path / fs::path(p); +#else std::basic_string_view s((const char8_t*)p.data(), p.size()); return path / fs::path(s); +#endif // __ANDROID__ } static fs::path GetPath(const fs::path& path) { diff --git a/src/gui/guiWrapper.h b/src/gui/guiWrapper.h index 90d2ffae..cfa53b68 100644 --- a/src/gui/guiWrapper.h +++ b/src/gui/guiWrapper.h @@ -6,7 +6,7 @@ struct WindowHandleInfo { #if BOOST_OS_WINDOWS std::atomic hwnd; -#elif BOOST_OS_LINUX +#elif BOOST_OS_LINUX && !__ANDROID__ enum class Backend { X11, diff --git a/src/imgui/imgui_impl_opengl3.cpp b/src/imgui/imgui_impl_opengl3.cpp index 96cf6ce7..44020f1c 100644 --- a/src/imgui/imgui_impl_opengl3.cpp +++ b/src/imgui/imgui_impl_opengl3.cpp @@ -95,6 +95,7 @@ #endif #endif +/* // GL includes #if defined(IMGUI_IMPL_OPENGL_ES2) #include @@ -119,10 +120,10 @@ //#include IMGUI_IMPL_OPENGL_LOADER_CUSTOM"glext.h""glext.h" #endif #endif - +*/ // Desktop GL has glDrawElementsBaseVertex() which GL ES and WebGL don't have. #if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) -#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 0 +#define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 1 // 0 #else #define IMGUI_IMPL_OPENGL_HAS_DRAW_WITH_BASE_VERTEX 1 #endif diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt index 8fc29044..5fed8891 100644 --- a/src/input/CMakeLists.txt +++ b/src/input/CMakeLists.txt @@ -15,10 +15,6 @@ add_library(CemuInput api/DSU/DSUControllerProvider.h api/DSU/DSUMessages.h api/DSU/DSUMessages.cpp - api/SDL/SDLController.cpp - api/SDL/SDLControllerProvider.cpp - api/SDL/SDLController.h - api/SDL/SDLControllerProvider.h api/Keyboard/KeyboardControllerProvider.h api/Keyboard/KeyboardControllerProvider.cpp api/Keyboard/KeyboardController.cpp @@ -73,6 +69,15 @@ if(WIN32) ) endif() +if(ENABLE_SDL) + target_sources(CemuInput PRIVATE + api/SDL/SDLController.cpp + api/SDL/SDLControllerProvider.cpp + api/SDL/SDLController.h + api/SDL/SDLControllerProvider.h + ) +endif() + target_include_directories(CemuInput PUBLIC "../") target_link_libraries(CemuInput PRIVATE @@ -85,5 +90,8 @@ target_link_libraries(CemuInput PRIVATE Boost::program_options glm::glm pugixml::pugixml - SDL2::SDL2 ) + +if(ENABLE_SDL) + target_link_libraries(CemuInput PRIVATE SDL2::SDL2) +endif() diff --git a/src/input/ControllerFactory.cpp b/src/input/ControllerFactory.cpp index 70a85a2d..18fa297a 100644 --- a/src/input/ControllerFactory.cpp +++ b/src/input/ControllerFactory.cpp @@ -5,7 +5,9 @@ #include "input/emulated/ClassicController.h" #include "input/emulated/WiimoteController.h" +#if HAS_SDL #include "input/api/SDL/SDLController.h" +#endif // HAS_SDL #include "input/api/Keyboard/KeyboardController.h" #include "input/api/DSU/DSUController.h" #include "input/api/GameCube/GameCubeController.h" diff --git a/src/input/api/SDL/SDLControllerProvider.h b/src/input/api/SDL/SDLControllerProvider.h index b736ba75..4101bb7a 100644 --- a/src/input/api/SDL/SDLControllerProvider.h +++ b/src/input/api/SDL/SDLControllerProvider.h @@ -1,11 +1,9 @@ #pragma once +#if HAS_SDL #include #include "input/motion/MotionHandler.h" #include "input/api/ControllerProvider.h" -#ifndef HAS_SDL -#define HAS_SDL 1 -#endif static bool operator==(const SDL_JoystickGUID& g1, const SDL_JoystickGUID& g2) { @@ -52,3 +50,5 @@ private: std::array m_motion_tracking{}; }; + +#endif // HAS_SDL diff --git a/src/input/emulated/ClassicController.cpp b/src/input/emulated/ClassicController.cpp index e218cded..ac387193 100644 --- a/src/input/emulated/ClassicController.cpp +++ b/src/input/emulated/ClassicController.cpp @@ -1,7 +1,9 @@ #include "input/emulated/ClassicController.h" #include "input/api/Controller.h" +#if HAS_SDL #include "input/api/SDL/SDLController.h" +#endif // HAS_SDL ClassicController::ClassicController(size_t player_index) : WPADController(player_index, kDataFormat_CLASSIC) @@ -130,6 +132,7 @@ bool ClassicController::set_default_mapping(const std::shared_ptr> mapping; switch (controller->api()) { +#if HAS_SDL case InputAPI::SDLController: { const auto sdl_controller = std::static_pointer_cast(controller); if (sdl_controller->get_guid() == SDLController::kLeftJoyCon) @@ -206,6 +209,7 @@ bool ClassicController::set_default_mapping(const std::shared_ptr& c std::vector> mapping; switch (controller->api()) { +#if HAS_SDL case InputAPI::SDLController: { const auto sdl_controller = std::static_pointer_cast(controller); if (sdl_controller->get_guid() == SDLController::kLeftJoyCon) @@ -219,6 +222,7 @@ bool ProController::set_default_mapping(const std::shared_ptr& c } break; } +#endif // HAS_SDL case InputAPI::XInput: { mapping = diff --git a/src/input/emulated/VPADController.cpp b/src/input/emulated/VPADController.cpp index eff221ef..a8395632 100644 --- a/src/input/emulated/VPADController.cpp +++ b/src/input/emulated/VPADController.cpp @@ -1,6 +1,8 @@ #include "input/emulated/VPADController.h" #include "input/api/Controller.h" +#if HAS_SDL #include "input/api/SDL/SDLController.h" +#endif // HAS_SDL #include "gui/guiWrapper.h" #include "input/InputManager.h" #include "Cafe/HW/Latte/Core/Latte.h" @@ -510,6 +512,7 @@ bool VPADController::set_default_mapping(const std::shared_ptr& std::vector> mapping; switch (controller->api()) { +#if HAS_SDL case InputAPI::SDLController: { const auto sdl_controller = std::static_pointer_cast(controller); if (sdl_controller->get_guid() == SDLController::kLeftJoyCon) @@ -633,6 +636,7 @@ bool VPADController::set_default_mapping(const std::shared_ptr& } break; } +#endif // HAS_SDL case InputAPI::XInput: { mapping = diff --git a/src/main.cpp b/src/main.cpp index aba8c811..cf7f7c65 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,8 +31,10 @@ #pragma comment(lib,"Dbghelp.lib") #endif +#if HAS_SDL #define SDL_MAIN_HANDLED #include +#endif #if BOOST_OS_LINUX #define _putenv(__s) putenv((char*)(__s)) @@ -329,7 +331,7 @@ int main(int argc, char* argv[]) int main(int argc, char *argv[]) { -#if BOOST_OS_LINUX +#if BOOST_OS_LINUX && !__ANDROID__ XInitThreads(); #endif if (!LaunchSettings::HandleCommandline(argc, argv)) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index f1bb09a6..599db860 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -74,7 +74,11 @@ if(WIN32) target_sources(CemuUtil PRIVATE MemMapper/MemMapperWin.cpp) target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoWin.cpp) elseif(UNIX) - target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp) + if(ANDROID) + target_sources(CemuUtil PRIVATE Fiber/FiberBoost.cpp) + else() + target_sources(CemuUtil PRIVATE Fiber/FiberUnix.cpp) + endif() target_sources(CemuUtil PRIVATE MemMapper/MemMapperUnix.cpp) target_sources(CemuUtil PRIVATE SystemInfo/SystemInfoUnix.cpp) if(NOT APPLE) @@ -90,6 +94,10 @@ set_property(TARGET CemuUtil PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$ + +#include "Fiber.h" + +static constexpr size_t stackSize = 2 * 1024 * 1024; + +struct FiberImpl +{ + bool isThread = false; + void* userParam; + void* stack; + std::function entryPoint; + boost::context::detail::fcontext_t context{}; + FiberImpl* previousFiber; + FiberImpl() + { + stack = malloc(stackSize); + } + static void start(boost::context::detail::transfer_t transfer) + { + FiberImpl* fiber = static_cast(transfer.data); + fiber->previousFiber->context = transfer.fctx; + fiber->previousFiber = nullptr; + fiber->entryPoint(fiber->userParam); + } + ~FiberImpl() + { + free(stack); + } +}; +thread_local Fiber* sCurrentFiber{}; + +Fiber::Fiber(void (*FiberEntryPoint)(void* userParam), void* userParam, void* privateData) + : m_privateData(privateData) +{ + FiberImpl* impl = new FiberImpl; + impl->entryPoint = FiberEntryPoint; + impl->userParam = userParam; + impl->context = boost::context::detail::make_fcontext(reinterpret_cast(impl->stack) + stackSize, stackSize, FiberImpl::start); + m_implData = impl; +} + +Fiber::Fiber(void* privateData) + : m_privateData(privateData) +{ + m_implData = new FiberImpl; +} + +Fiber::~Fiber() +{ + delete static_cast(m_implData); +} + +Fiber* Fiber::PrepareCurrentThread(void* privateData) +{ + sCurrentFiber = new Fiber(privateData); + return sCurrentFiber; +} + +void Fiber::Switch(Fiber& targetFiber) +{ + if (&targetFiber == sCurrentFiber) + { + return; + } + FiberImpl* _currentFiber = static_cast(sCurrentFiber->m_implData); + FiberImpl* _targetFiber = static_cast(targetFiber.m_implData); + _targetFiber->previousFiber = _currentFiber; + sCurrentFiber = &targetFiber; + auto transfer = boost::context::detail::jump_fcontext(_targetFiber->context, _targetFiber); + if (_currentFiber->previousFiber == nullptr) + { + return; + } + _currentFiber->previousFiber->context = transfer.fctx; + _currentFiber->previousFiber->previousFiber = nullptr; +} + +void* Fiber::GetFiberPrivateData() +{ + return sCurrentFiber->m_privateData; +} diff --git a/vcpkg.json b/vcpkg.json index d0facf8b..e83d3bcc 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -11,7 +11,10 @@ "default-features": false }, "rapidjson", - "sdl2", + { + "name": "sdl2", + "platform": "!android" + }, "boost-tokenizer", "boost-container", "boost-program-options", @@ -24,6 +27,10 @@ "boost-ptr-container", "boost-property-tree", "boost-static-string", + { + "name": "boost-context", + "platform" : "android" + }, "boost-random", "fmt", "libpng", @@ -35,7 +42,8 @@ "zstd", { "name": "wxwidgets", - "default-features": false + "default-features": false, + "platform": "!android" }, "openssl", { From 05f3444207eb679021a5be0a64d2750659c5a6ef Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Thu, 8 Jun 2023 08:54:05 +0300 Subject: [PATCH 08/30] Add android project to src --- src/android/.gitignore | 15 ++ src/android/app/.gitignore | 1 + src/android/app/build.gradle | 65 ++++++ src/android/app/proguard-rules.pro | 21 ++ .../cemu/Cemu/ExampleInstrumentedTest.java | 26 +++ src/android/app/src/main/AndroidManifest.xml | 26 +++ .../java/info/cemu/Cemu/MainActivity.java | 36 ++++ .../drawable-v24/ic_launcher_foreground.xml | 30 +++ .../res/drawable/ic_launcher_background.xml | 170 ++++++++++++++++ .../app/src/main/res/layout/activity_main.xml | 19 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes .../app/src/main/res/values-night/themes.xml | 16 ++ .../app/src/main/res/values/colors.xml | 10 + .../app/src/main/res/values/strings.xml | 3 + .../app/src/main/res/values/themes.xml | 16 ++ .../app/src/main/res/xml/backup_rules.xml | 13 ++ .../main/res/xml/data_extraction_rules.xml | 19 ++ .../java/info/cemu/Cemu/ExampleUnitTest.java | 17 ++ src/android/build.gradle | 5 + src/android/gradle.properties | 21 ++ src/android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + src/android/gradlew | 185 ++++++++++++++++++ src/android/gradlew.bat | 89 +++++++++ src/android/settings.gradle | 16 ++ 36 files changed, 837 insertions(+) create mode 100644 src/android/.gitignore create mode 100644 src/android/app/.gitignore create mode 100644 src/android/app/build.gradle create mode 100644 src/android/app/proguard-rules.pro create mode 100644 src/android/app/src/androidTest/java/info/cemu/Cemu/ExampleInstrumentedTest.java create mode 100644 src/android/app/src/main/AndroidManifest.xml create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java create mode 100644 src/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 src/android/app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 src/android/app/src/main/res/layout/activity_main.xml create mode 100644 src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 src/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 src/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 src/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 src/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 src/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 src/android/app/src/main/res/values-night/themes.xml create mode 100644 src/android/app/src/main/res/values/colors.xml create mode 100644 src/android/app/src/main/res/values/strings.xml create mode 100644 src/android/app/src/main/res/values/themes.xml create mode 100644 src/android/app/src/main/res/xml/backup_rules.xml create mode 100644 src/android/app/src/main/res/xml/data_extraction_rules.xml create mode 100644 src/android/app/src/test/java/info/cemu/Cemu/ExampleUnitTest.java create mode 100644 src/android/build.gradle create mode 100644 src/android/gradle.properties create mode 100644 src/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 src/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 src/android/gradlew create mode 100644 src/android/gradlew.bat create mode 100644 src/android/settings.gradle diff --git a/src/android/.gitignore b/src/android/.gitignore new file mode 100644 index 00000000..aa724b77 --- /dev/null +++ b/src/android/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/src/android/app/.gitignore b/src/android/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/src/android/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle new file mode 100644 index 00000000..409aab19 --- /dev/null +++ b/src/android/app/build.gradle @@ -0,0 +1,65 @@ +plugins { + id 'com.android.application' +} + +android { + namespace 'info.cemu.Cemu' + compileSdk 33 + ndkVersion '25.2.9519653' + defaultConfig { + applicationId "info.cemu.Cemu" + minSdk 30 + targetSdk 33 + versionCode 1 + versionName "1.0" + ndk { +// abiFilters("x86_64", "arm64-v8a") + abiFilters("x86_64") + } + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + externalNativeBuild { + cmake { + arguments( + '-DENABLE_VCPKG=ON', + '-DVCPKG_TARGET_ANDROID=ON', + '-DENABLE_SDL=OFF', + '-DENABLE_OPENGL=OFF', + '-DENABLE_WXWIDGETS=OFF', + '-DBUNDLE_SPEEX=ON', + '-DENABLE_DISCORD_RPC=OFF', + '-DENABLE_WAYLAND=OFF' + ) + } + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + externalNativeBuild { + cmake { + path file('../../../CMakeLists.txt') + version '3.22.1' + } + } + buildFeatures { + viewBinding true + } +} + +dependencies { + + implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'com.google.android.material:material:1.9.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' +} \ No newline at end of file diff --git a/src/android/app/proguard-rules.pro b/src/android/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/src/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/src/android/app/src/androidTest/java/info/cemu/Cemu/ExampleInstrumentedTest.java b/src/android/app/src/androidTest/java/info/cemu/Cemu/ExampleInstrumentedTest.java new file mode 100644 index 00000000..a681e31f --- /dev/null +++ b/src/android/app/src/androidTest/java/info/cemu/Cemu/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package info.cemu.Cemu; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("info.cemu.Cemu", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..06454137 --- /dev/null +++ b/src/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java b/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java new file mode 100644 index 00000000..5df0c452 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java @@ -0,0 +1,36 @@ +package info.cemu.Cemu; + +import androidx.appcompat.app.AppCompatActivity; + +import android.os.Bundle; +import android.widget.TextView; + +import info.cemu.Cemu.databinding.ActivityMainBinding; + +public class MainActivity extends AppCompatActivity { + + // Used to load the 'Cemu' library on application startup. + static { + System.loadLibrary("Cemu"); + } + + private ActivityMainBinding binding; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + // Example of a call to a native method + TextView tv = binding.sampleText; + tv.setText(stringFromJNI()); + } + + /** + * A native method that is implemented by the 'Cemu' native library, + * which is packaged with this application. + */ + public native String stringFromJNI(); +} \ No newline at end of file diff --git a/src/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/src/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/drawable/ic_launcher_background.xml b/src/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/activity_main.xml b/src/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..0fdf9852 --- /dev/null +++ b/src/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..6f3b755b --- /dev/null +++ b/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..6f3b755b --- /dev/null +++ b/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/src/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c209e78ecd372343283f4157dcfd918ec5165bb3 GIT binary patch literal 1404 zcmWIYbaN|VWnc(*bqWXzu!!JdU|xt`5!v_jjvkEam13D%HG@k-IMc zNLB6Ts|;5bzSwJe>ULbl1cvf&ZkhaCoSq2>@AZ5-D>jj1lIT2H&dag$gn7N+e4L{u z^V=qU9`n4<|KGj+Jg1Vc;+*Ym8}@ms^PajGxhz`fUcGqB^ot%&0-(G6 ztJqa}H}n3HU3Yip`SGVeoN_0jHpk{$^4G>a6%8p%x7&Z)J5^*02Bzg(emT zh6~~ha~ZWf7(5v43`IJe7AY!6)06s|5LTRZ~a|9(#3-?A(9e&YZ4?>X2s?GW>o|33SDelz`B z`+wn|Q;!w?=CKR)$n9qK&JB=|i?B8=Za(_yby{8ahEC_FQc?|fX6jq-S#{;T*P>@x z_ZT?env#-TMm+56J zYxbSv3`wunSg8L0QM8}=O*rp?-VOhM|NEg<)7%*xHvh-siA*kcCjMS>GB?P(>_@}3 z)Ft&xoDRNt&3eFh)jsaGt6V1Y-utsGX3D{BPrGiMe#bFC=cA;8JMEoLi`oHXxI)DE^oHMbkaEEw% z-9tKtyZ;ro&wXg4x@O8`m1O~q z?MexGg12&nX4y|S@pnFV^V}u9giQ-GT$3$KnU6R18RkeC3%K&!v-YvBIofAmw>PFs zIdS)#VNole5AMDC;{Wfgucb?`mgwZOH*=)K)xFxh zvAbsbOYcV^yis0yb^(2L-iLG^EU>@ZcYlA->$iWR*2l$qNu~2Y6n&DlA@j?-eeQ>s z$tnK&Z05FhmCbjTD+dy9O%PYyrL7Vlbht#oNZ2QS;ASZ@?QSi@KIA`)Q{Plar-EBMn zOue{8S#RsoI9{I8-1%wjYL3ra-WXN>myuB|k`TLaRw0(}Q{v*)neU%|Uf=%kE$@XZ zhG9pKE&f~)a@RGze*OGS1~2BsylnCL*)X3i|99J|eC4QK#}-~?>BH&Aafd zWjZOh{NVc@|Mx8WeQ&M$PqIskKh9jN|JQv<&;0)q`yZ~E&-DGzd;15MW#|73p4FDw z7Oym+LiWGhsn`4Zdv7t_;1XiVJF+=y-{rn5k2YR9d}BwU-%Zx7^dl3uQO%=KtB6@_8OJ`?s99PuHc(Wv{Uvn57cQnzH^! z4YT&W4>v_49d0g>;5}1QsqLw*bguIK-eRRko_V#EY72aobT&L-pQO|$YAra)UWXy) z?H0HH>?%h$pNjLSyTHFTTZD1@o5kYirK&#Ospl5Bs^WBcdditu$Df4zc4P@8o+&!k z$5~=Hz27y&v^)8mahg-|JA1KhUV`UTT02EQ$+(1e7MN)IY?yFdc~#TV8Gl98qIT3X z<+w`qp4Rl)QQ*p5k{WN6_9(e-vDU^NGYy%0D%Eo$*=nm_Iu+cp7Fo9EyM*zH8S<-( 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 AkN^Mx literal 0 HcmV?d00001 diff --git a/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/src/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..948a3070fe34c611c42c0d3ad3013a0dce358be0 GIT binary patch literal 1900 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

( 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 literal 0 HcmV?d00001 diff --git a/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/src/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..9287f5083623b375139afb391af71cc533a7dd37 GIT binary patch literal 5914 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

0vl^(ubK57i3$j`RXVdItc2Zu zTjP%3T{qqDyHu$1wf>en`}f$lI?l)hfy+`aCTxu8`Mp4Wt8~lzsk7fIKUzdbU&D@u>@#|8JoKt2mbv8|~*&2E%ct_*EIJ>)7o&PKE5_$1H zYTdgu^~m!0A4{%YZ`c&qZ)$yr|AXm!sqXjcKR?_Pn|Ao^jNS9)1$Q|wns@3F%fsvg zwSO(SHf}Wd&gY@5dZETG=E3!B4m$1-}j)^|n6tq;mOd3k8c0DH_GHdj+bC#}?wC!#j7mQS#vf_fXiT$x+$0-Ud(~5MTubmb>WqqsT;k{xpsIQhc5wmzv^leMXZ>rUpkvod!E z%4X!{CT@DfUb}1l9;OF%&cZj$=~`?izExW$`!W#MmrU&bU9x zY4?Y|ihvD`aVe?!Jd*bn>a_Ljw%A+cxQM63#5tgD>7pvIfk zXZB`A(+B?|xK;mhgAxy6j8m5a|tYYQtz-&)`wciH>w!z4Jl6EbdLNgW&t4n&Z^`r` zg&vWF3I>a?I;mM9pQdx?h+g`4^#9q@#m5&cUv`wUW67(N(f8cqnde#^*{|z4`#8&% z$(*MTuC#KwaPVof`>D`&U$yg@H74@YWB+XNzOk9HzHI)?S*96(9jadGnl+RhV?HYU zTPymOU!2^Y9Y;SYsn1QEpStG70rM^O4*D71J+(bX{#n9KLDg&XW+cq#d%E33PUWv_ zkfOTshO5hBt#1C8*W^yL{vnXQVRqiLErQMrj6U1<_5@j|Em*A4E4-te^ZzpYDI0B` zXg;x>w9{g(u&mK%{^Fz8n{xBlOZuI;!E!86S+BwIO2Xl&Fh%#ZoBUQU_O0g4%ANk> z0Jmq=?VG;}VuQcWs7ieBJAdXY(fhi`7ADvReRQ>AUivA=PwM}&`iLJJJJZ{_&k3Y2 zUvhnu->Tx7bEg!!9%6moQp;o7lU20XJ=QDEK<4}Dx`6F-nzAlROmUdnwpX%JBgyHE&EJd_^1F8lZE|Tb`4Z9o=_5<>!KzQ} zGZoDj*B>l-_{cMi-TF`Qmi1X-=QLK8d3<{luV|^Id8CphQgh85osBxngzXY9@84_h z%f`4XHqy%Sk8jJOIRCXp`#HYZ9tjb8r7sFp_47S~Y8LMZ0|ACD*T9*?vNtsq>c#FZ;4AF83!N-}7Zh7R+l= zw!4tMaFcX!P3Els8SK9LLhRyJK z+&t%0aQM50`k&8S%~Uwp?rm21|LFNWlV+G(ACydR-+6qUWz*kv3iZ{C%If{&>euW) z@@W5$w|~FeitEW*v$L+4y5z_58%Bq(G3{FX-GAnci|2Se?q;?>GnLW&F(+=#|9Z#w z-#_o;-&VrrqOwwO%B-^WHLF&}EjZj>w|%nOHqFIdHlY~LM|@kt^cB?v?aRN!JSJaHjLeW zb$GNz$NM?U8z(oNo3`r8e%lS3CEre$zVUyZ&{wym*PqV*QMTjIf~G4kJUQ0C+9_cC zLSfq{(=7&@u0G04(^)DtZ};(To{cq(j|-+htN*jEpuxFk0?Ru8XP3^z-&SNkURt^2 zTSfZcN5x^>ML|ky1rH}oViJ7+@cetDleV!hBksSwEn~4q@Xz`GmCrjHOK)GEd0p-B z(Y6SS8poDMuE(=E|4rGmcvp9MsGHU9V8)Ek>HE*irk%F9rK7RU-2N}yiz$uUO57Qx zXRAv!tdNVQd!7^~xNg;>=ojmFGC8tun)m$oP3PKhc*onS@K5?rr^}yu zUmUrRUESJJ=>F0}m;TMP>i;SjwwR~)_nQ*mc_z*-WwSXs6&fs_bqPz9s|2|0xg0$2 t@u$Pda=rYAtrOikX7%&GRsG&;Z1Lh{&yL;Ao6ROYUN$pxVgmyL003>zdZqvX literal 0 HcmV?d00001 diff --git a/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/src/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..9126ae37cbc3587421d6889eadd1d91fbf1994d4 GIT binary patch literal 7778 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 literal 0 HcmV?d00001 diff --git a/src/android/app/src/main/res/values-night/themes.xml b/src/android/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..c0c8b990 --- /dev/null +++ b/src/android/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/values/colors.xml b/src/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..f8c6127d --- /dev/null +++ b/src/android/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..24871da5 --- /dev/null +++ b/src/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Cemu + \ No newline at end of file diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..8209cef4 --- /dev/null +++ b/src/android/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/xml/backup_rules.xml b/src/android/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 00000000..fa0f996d --- /dev/null +++ b/src/android/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/xml/data_extraction_rules.xml b/src/android/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 00000000..9ee9997b --- /dev/null +++ b/src/android/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/src/android/app/src/test/java/info/cemu/Cemu/ExampleUnitTest.java b/src/android/app/src/test/java/info/cemu/Cemu/ExampleUnitTest.java new file mode 100644 index 00000000..a673d6c5 --- /dev/null +++ b/src/android/app/src/test/java/info/cemu/Cemu/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package info.cemu.Cemu; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/src/android/build.gradle b/src/android/build.gradle new file mode 100644 index 00000000..a100ce04 --- /dev/null +++ b/src/android/build.gradle @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '8.0.2' apply false + id 'com.android.library' version '8.0.2' apply false +} \ No newline at end of file diff --git a/src/android/gradle.properties b/src/android/gradle.properties new file mode 100644 index 00000000..3e927b11 --- /dev/null +++ b/src/android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/src/android/gradle/wrapper/gradle-wrapper.jar b/src/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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 YhOdRl$_CP6#$d+qN{E5M`3i^!03mLhYXATM literal 0 HcmV?d00001 diff --git a/src/android/gradle/wrapper/gradle-wrapper.properties b/src/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..fa497158 --- /dev/null +++ b/src/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jun 07 12:59:34 EEST 2023 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/src/android/gradlew b/src/android/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/src/android/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/src/android/gradlew.bat b/src/android/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/src/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/android/settings.gradle b/src/android/settings.gradle new file mode 100644 index 00000000..38feecc3 --- /dev/null +++ b/src/android/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "Cemu" +include ':app' From a03ce9a7465f236845c52063b89e88a984340dd7 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sat, 10 Jun 2023 13:44:36 +0300 Subject: [PATCH 09/30] Fix android build --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 3 +- src/asm/CMakeLists.txt | 4 +- src/gui/AndroidGui/AndroidGuiWrapper.cpp | 113 ++++++++++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 src/gui/AndroidGui/AndroidGuiWrapper.cpp diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 166ac016..36ff0331 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -1285,7 +1285,7 @@ VkSurfaceKHR VulkanRenderer::CreateAndroidSurface(VkInstance instance, ANativeWi VkResult err; if ((err = vkCreateAndroidSurfaceKHR(instance, &sci, nullptr, &result)) != VK_SUCCESS) { - forceLog_printf("Cannot create an Android Vulkan surface: %d", (sint32)err); + cemuLog_log(LogType::Force, "Cannot create an Android Vulkan surface: {}", (sint32)err); throw std::runtime_error(fmt::format("Cannot create an Android Vulkan surface: {}", err)); } @@ -1358,6 +1358,7 @@ VkSurfaceKHR VulkanRenderer::CreateFramebufferSurface(VkInstance instance, struc return CreateWinSurface(instance, windowInfo.hwnd); #elif BOOST_OS_LINUX #if __ANDROID__ + return CreateAndroidSurface(instance, static_cast(windowInfo.handle)); #else if(windowInfo.backend == WindowHandleInfo::Backend::X11) return CreateXlibSurface(instance, windowInfo.xlib_display, windowInfo.xlib_window); diff --git a/src/asm/CMakeLists.txt b/src/asm/CMakeLists.txt index fa9f74a9..dfe2a367 100644 --- a/src/asm/CMakeLists.txt +++ b/src/asm/CMakeLists.txt @@ -16,13 +16,13 @@ if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") set_property(TARGET CemuAsm PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - elseif(ANDROID) - set(CMAKE_ASM_NASM_COMPILE_OBJECT " -g dwarf2 -f elf64 -o ") else() # NASM if (APPLE) set(CMAKE_ASM_NASM_COMPILE_OBJECT " -g -Fdwarf -f macho64 --prefix _ -o ") + elseif(ANDROID) + set(CMAKE_ASM_NASM_COMPILE_OBJECT " -g dwarf2 -f elf64 -o ") else() set(CMAKE_ASM_NASM_COMPILE_OBJECT " -g -Fdwarf -f elf64 -o ") endif() diff --git a/src/gui/AndroidGui/AndroidGuiWrapper.cpp b/src/gui/AndroidGui/AndroidGuiWrapper.cpp new file mode 100644 index 00000000..ae89fe2f --- /dev/null +++ b/src/gui/AndroidGui/AndroidGuiWrapper.cpp @@ -0,0 +1,113 @@ +#include "gui/guiWrapper.h" + + +bool g_inputConfigWindowHasFocus; +WindowInfo g_window_info; + +void gui_loggingWindowLog(std::string_view filter, std::string_view message) +{ +} +void gui_loggingWindowLog(std::string_view message) +{ +} + +void gui_create() +{ +} + +WindowInfo& gui_getWindowInfo() +{ + return g_window_info; +} + +void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps) +{ +} +void gui_getWindowSize(int& w, int& h) +{ +} +void gui_getPadWindowSize(int& w, int& h) +{ +} +void gui_getWindowPhysSize(int& w, int& h) +{ +} +void gui_getPadWindowPhysSize(int& w, int& h) +{ +} +double gui_getWindowDPIScale() +{ +} +double gui_getPadDPIScale() +{ +} +bool gui_isPadWindowOpen() +{ +} +bool gui_isKeyDown(uint32 key) +{ + return false; +} +bool gui_isKeyDown(PlatformKeyCodes key) +{ + return false; +} + +void gui_notifyGameLoaded() +{ +} +void gui_notifyGameExited() +{ +} + +bool gui_isFullScreen() +{ + return false; +} + +void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, class wxWindow* wxw) +{ +} + +void gui_requestGameListRefresh() +{ +} + +std::string gui_RawKeyCodeToString(uint32 keyCode) +{ +} + +bool gui_saveScreenshotToFile(const fs::path& imagePath, std::vector& data, int width, int height) +{ +} +bool gui_saveScreenshotToClipboard(std::vector& data, int width, int height) +{ +} +/* + * Returns true if a screenshot request is queued + * Once this function has returned true, it will reset back to + * false until the next time a screenshot is requested + */ +bool gui_hasScreenshotRequest() +{ +} + +// debugger stuff +void debuggerWindow_updateViewThreadsafe2() +{ +} +void debuggerWindow_notifyDebugBreakpointHit2() +{ +} +void debuggerWindow_notifyRun() +{ +} +void debuggerWindow_moveIP() +{ +} +void debuggerWindow_notifyModuleLoaded(void* module) +{ +} +void debuggerWindow_notifyModuleUnloaded(void* module) +{ +} From 12e7f9956c828a3ef2d3f8191fb58f867f1b0ea2 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Sun, 11 Jun 2023 07:33:07 +0300 Subject: [PATCH 10/30] Add icon & title loader classes and JNI project --- src/CMakeLists.txt | 33 +- src/android/app/build.gradle | 38 +- src/android/app/src/main/cpp/CMakeLists.txt | 12 + src/android/app/src/main/cpp/native-lib.cpp | 10 + .../java/info/cemu/Cemu/MainActivity.java | 3 +- src/gui/AndroidGui/CMakeLists.txt | 29 + src/gui/AndroidGui/GameIconLoader.cpp | 70 + src/gui/AndroidGui/GameIconLoader.h | 25 + src/gui/AndroidGui/GameTitleLoader.cpp | 114 + src/gui/AndroidGui/GameTitleLoader.h | 48 + src/gui/AndroidGui/Image.cpp | 41 + src/gui/AndroidGui/Image.h | 18 + src/gui/AndroidGui/OpenGLCanvas.cpp | 3 + src/gui/AndroidGui/stb_image.h | 7897 +++++++++++++++++ src/gui/CMakeLists.txt | 5 + src/main.cpp | 3 +- 16 files changed, 8316 insertions(+), 33 deletions(-) create mode 100644 src/android/app/src/main/cpp/CMakeLists.txt create mode 100644 src/android/app/src/main/cpp/native-lib.cpp create mode 100644 src/gui/AndroidGui/CMakeLists.txt create mode 100644 src/gui/AndroidGui/GameIconLoader.cpp create mode 100644 src/gui/AndroidGui/GameIconLoader.h create mode 100644 src/gui/AndroidGui/GameTitleLoader.cpp create mode 100644 src/gui/AndroidGui/GameTitleLoader.h create mode 100644 src/gui/AndroidGui/Image.cpp create mode 100644 src/gui/AndroidGui/Image.h create mode 100644 src/gui/AndroidGui/OpenGLCanvas.cpp create mode 100644 src/gui/AndroidGui/stb_image.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 308ceafc..3e324308 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,6 +43,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) add_subdirectory(Common) add_subdirectory(gui) +if(ANDROID) + add_subdirectory(android/app/src/main/cpp) +endif() add_subdirectory(Cafe) add_subdirectory(Cemu) add_subdirectory(config) @@ -53,21 +56,27 @@ add_subdirectory(imgui) add_subdirectory(resource) add_subdirectory(asm) -add_executable(CemuBin - main.cpp - mainLLE.cpp -) +if(ANDROID) + add_library(CemuBin + main.cpp + mainLLE.cpp + ) +else() + add_executable(CemuBin + main.cpp + mainLLE.cpp + ) -if(WIN32) - target_sources(CemuBin PRIVATE - resource/cemu.rc -) + if(WIN32) + target_sources(CemuBin PRIVATE + resource/cemu.rc + ) + endif() + set_property(TARGET CemuBin PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET CemuBin PROPERTY WIN32_EXECUTABLE $>) + set(OUTPUT_NAME "Cemu_$>") endif() -set_property(TARGET CemuBin PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") -set_property(TARGET CemuBin PROPERTY WIN32_EXECUTABLE $>) -set(OUTPUT_NAME "Cemu_$>") - if (MACOS_BUNDLE) set_property(TARGET CemuBin PROPERTY MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resource/MacOSXBundleInfo.plist.in") diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 409aab19..3c266dee 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -4,12 +4,12 @@ plugins { android { namespace 'info.cemu.Cemu' - compileSdk 33 + compileSdk 34 ndkVersion '25.2.9519653' defaultConfig { applicationId "info.cemu.Cemu" minSdk 30 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0" ndk { @@ -17,20 +17,6 @@ android { abiFilters("x86_64") } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - externalNativeBuild { - cmake { - arguments( - '-DENABLE_VCPKG=ON', - '-DVCPKG_TARGET_ANDROID=ON', - '-DENABLE_SDL=OFF', - '-DENABLE_OPENGL=OFF', - '-DENABLE_WXWIDGETS=OFF', - '-DBUNDLE_SPEEX=ON', - '-DENABLE_DISCORD_RPC=OFF', - '-DENABLE_WAYLAND=OFF' - ) - } - } } buildTypes { @@ -45,8 +31,25 @@ android { } externalNativeBuild { cmake { - path file('../../../CMakeLists.txt') version '3.22.1' + path file('../../../CMakeLists.txt') + } + } + defaultConfig { + externalNativeBuild { + cmake { + arguments( + '-DENABLE_VCPKG=ON', + '-DVCPKG_TARGET_ANDROID=ON', + '-DENABLE_SDL=OFF', + '-DENABLE_WXWIDGETS=OFF', + '-DENABLE_OPENGL=OFF', + '-DBUNDLE_SPEEX=ON', + '-DENABLE_DISCORD_RPC=OFF', + '-DENABLE_WAYLAND=OFF' + ) + abiFilters("x86_64") + } } } buildFeatures { @@ -55,7 +58,6 @@ android { } dependencies { - implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/src/android/app/src/main/cpp/CMakeLists.txt b/src/android/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..accf2fd9 --- /dev/null +++ b/src/android/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library( + CemuAndroid + SHARED + native-lib.cpp +) + +target_link_libraries( + CemuAndroid + PRIVATE + CemuCommon + CemuAndroidGui +) diff --git a/src/android/app/src/main/cpp/native-lib.cpp b/src/android/app/src/main/cpp/native-lib.cpp new file mode 100644 index 00000000..7e5d3f17 --- /dev/null +++ b/src/android/app/src/main/cpp/native-lib.cpp @@ -0,0 +1,10 @@ +#include +#include + +extern "C" JNIEXPORT jstring JNICALL +Java_com_example_myapplication_MainActivity_stringFromJNI( + JNIEnv *env, + jobject /* this */) { + std::string hello = "Hello from C++"; + return env->NewStringUTF(hello.c_str()); +} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java b/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java index 5df0c452..0764945c 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java @@ -9,9 +9,8 @@ import info.cemu.Cemu.databinding.ActivityMainBinding; public class MainActivity extends AppCompatActivity { - // Used to load the 'Cemu' library on application startup. static { - System.loadLibrary("Cemu"); + System.loadLibrary("CemuAndroid"); } private ActivityMainBinding binding; diff --git a/src/gui/AndroidGui/CMakeLists.txt b/src/gui/AndroidGui/CMakeLists.txt new file mode 100644 index 00000000..fb4c4360 --- /dev/null +++ b/src/gui/AndroidGui/CMakeLists.txt @@ -0,0 +1,29 @@ + +add_library(CemuAndroidGui + AndroidGuiWrapper.cpp + GameIconLoader.cpp + GameIconLoader.h + GameTitleLoader.cpp + GameTitleLoader.h + OpenGLCanvas.cpp + Image.cpp +) + +target_include_directories(CemuAndroidGui PUBLIC "../") + +target_link_libraries(CemuAndroidGui PRIVATE + CemuAudio + CemuCafe + CemuCommon + CemuComponents + CemuConfig + CemuInput + CemuResource + CemuUtil + Boost::headers + CURL::libcurl + libzip::zip + OpenSSL::Crypto + pugixml::pugixml + ZArchive::zarchive +) diff --git a/src/gui/AndroidGui/GameIconLoader.cpp b/src/gui/AndroidGui/GameIconLoader.cpp new file mode 100644 index 00000000..0599a126 --- /dev/null +++ b/src/gui/AndroidGui/GameIconLoader.cpp @@ -0,0 +1,70 @@ +#include "GameIconLoader.h" + +#include "Cafe/TitleList/TitleInfo.h" +#include "Cafe/TitleList/TitleList.h" + +GameIconLoader::GameIconLoader() { + m_loaderThread = std::thread(&GameIconLoader::loadGameIcons, this); +} + +GameIconLoader::~GameIconLoader() { + m_continueLoading = false; + m_condVar.notify_one(); + m_loaderThread.join(); +} + +void GameIconLoader::requestIcon(TitleId titleId) { + { + std::lock_guard lock(m_threadMutex); + m_iconsToLoad.emplace_front(titleId); + } + m_condVar.notify_one(); +} + +void GameIconLoader::loadGameIcons() { + while (m_continueLoading) { + TitleId titleId = 0; + { + std::unique_lock lock(m_threadMutex); + m_condVar.wait(lock, [this] { + return !m_iconsToLoad.empty() || !m_continueLoading; + }); + if (!m_continueLoading) + return; + titleId = m_iconsToLoad.front(); + m_iconsToLoad.pop_front(); + } + TitleInfo titleInfo; + if (!CafeTitleList::GetFirstByTitleId(titleId, titleInfo)) + continue; + + if (auto iconIt = m_iconCache.find(titleId); iconIt != m_iconCache.end()) { + if (m_onIconLoaded) m_onIconLoaded(titleId, iconIt->second); + continue; + } + + std::string tempMountPath = TitleInfo::GetUniqueTempMountingPath(); + if (!titleInfo.Mount(tempMountPath, "", FSC_PRIORITY_BASE)) + continue; + auto tgaData = fsc_extractFile((tempMountPath + "/meta/iconTex.tga").c_str()); + if (tgaData && tgaData->size() > 16) { + int width, height, channels; + Image image(tgaData.value()); + if (image.isOk()) { + if (m_onIconLoaded) m_onIconLoaded(titleId, image); + m_iconCache.emplace(titleId, std::move(image)); + } + } else { + cemuLog_log(LogType::Force, "Failed to load icon for title {:016x}", titleId); + } + titleInfo.Unmount(tempMountPath); + } +} + +void GameIconLoader::setOnIconLoaded(const std::function &onIconLoaded) { + { + std::lock_guard lock(m_threadMutex); + m_onIconLoaded = onIconLoaded; + } + m_condVar.notify_one(); +}; diff --git a/src/gui/AndroidGui/GameIconLoader.h b/src/gui/AndroidGui/GameIconLoader.h new file mode 100644 index 00000000..cf75142d --- /dev/null +++ b/src/gui/AndroidGui/GameIconLoader.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Cafe/TitleList/TitleId.h" +#include "Image.h" + +class GameIconLoader { + std::atomic_bool m_continueLoading = true; + std::condition_variable m_condVar; + std::thread m_loaderThread; + std::mutex m_threadMutex; + std::deque m_iconsToLoad; + std::map m_iconCache; + std::function m_onIconLoaded = nullptr; + + void loadGameIcons(); + +public: + GameIconLoader(); + + ~GameIconLoader(); + + void setOnIconLoaded(const std::function &onIconLoaded); + + void requestIcon(TitleId titleId); +}; diff --git a/src/gui/AndroidGui/GameTitleLoader.cpp b/src/gui/AndroidGui/GameTitleLoader.cpp new file mode 100644 index 00000000..2b09cdb5 --- /dev/null +++ b/src/gui/AndroidGui/GameTitleLoader.cpp @@ -0,0 +1,114 @@ +#include "GameTitleLoader.h" + +GameTitleLoader::GameTitleLoader() { + m_loaderThread = std::thread(&GameTitleLoader::loadGameTitles, this); +} + +void GameTitleLoader::queueTitle(TitleId titleId) { + { + std::lock_guard lock(m_threadMutex); + m_titlesToLoad.emplace_front(titleId); + } + m_condVar.notify_one(); +} + +void GameTitleLoader::setOnTitleLoaded(const std::function &onTitleLoaded) { + { + std::lock_guard lock(m_threadMutex); + m_onTitleLoaded = onTitleLoaded; + } + m_condVar.notify_one(); +} + +void GameTitleLoader::reloadGameTitles() { + if (m_callbackIdTitleList.has_value()) { + CafeTitleList::Refresh(); + CafeTitleList::UnregisterCallback(m_callbackIdTitleList.value()); + } + registerCallback(); +} + +GameTitleLoader::~GameTitleLoader() { + m_continueLoading = false; + m_condVar.notify_one(); + m_loaderThread.join(); + if (m_callbackIdTitleList.has_value()) + CafeTitleList::UnregisterCallback(m_callbackIdTitleList.value()); +} + +void GameTitleLoader::titleRefresh(TitleId titleId) { + GameInfo2 gameInfo = CafeTitleList::GetGameInfo(titleId); + if (!gameInfo.IsValid()) { + return; + } + TitleId baseTitleId = gameInfo.GetBaseTitleId(); + bool isNewEntry = false; + if (auto gameIndex = m_gameInfos.find(baseTitleId); gameIndex == m_gameInfos.end()) { + isNewEntry = true; + m_titleIds.push_back(baseTitleId); + m_gameInfos[baseTitleId] = Game(); + } + Game &game = m_gameInfos[baseTitleId]; + game.titleId = baseTitleId; + game.name = GetNameByTitleId(baseTitleId); + game.version = gameInfo.GetVersion(); + game.region = gameInfo.GetRegion(); + if (gameInfo.HasAOC()) { + game.dlc = gameInfo.GetAOCVersion(); + } + if (isNewEntry) { + iosu::pdm::GameListStat playTimeStat; + if (iosu::pdm::GetStatForGamelist(baseTitleId, playTimeStat)) + game.secondsPlayed = playTimeStat.numMinutesPlayed * 60; + if (m_onTitleLoaded) m_onTitleLoaded(game); + } else { + // TODO: Update + } +} + +void GameTitleLoader::loadGameTitles() { + while (m_continueLoading) { + TitleId titleId = 0; + { + std::unique_lock lock(m_threadMutex); + m_condVar.wait(lock, [this] { + return (!m_titlesToLoad.empty()) || + !m_continueLoading; + }); + if (!m_continueLoading) + return; + titleId = m_titlesToLoad.front(); + m_titlesToLoad.pop_front(); + } + titleRefresh(titleId); + } +} + +std::string GameTitleLoader::GetNameByTitleId(uint64 titleId) { + auto it = m_name_cache.find(titleId); + if (it != m_name_cache.end()) + return it->second; + TitleInfo titleInfo; + if (!CafeTitleList::GetFirstByTitleId(titleId, titleInfo)) + return "Unknown title"; + std::string name; + if (!GetConfig().GetGameListCustomName(titleId, name)) + name = titleInfo.GetTitleName(); + m_name_cache.emplace(titleId, name); + return name; +} + +void GameTitleLoader::registerCallback() { + m_callbackIdTitleList = CafeTitleList::RegisterCallback( + [](CafeTitleListCallbackEvent *evt, + void *ctx) { + static_cast(ctx)->HandleTitleListCallback(evt); + }, this); +} + +void GameTitleLoader::HandleTitleListCallback(CafeTitleListCallbackEvent *evt) { + if (evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_DISCOVERED || + evt->eventType == CafeTitleListCallbackEvent::TYPE::TITLE_REMOVED) { + queueTitle(evt->titleInfo->GetAppTitleId()); + } +} diff --git a/src/gui/AndroidGui/GameTitleLoader.h b/src/gui/AndroidGui/GameTitleLoader.h new file mode 100644 index 00000000..9f92deb8 --- /dev/null +++ b/src/gui/AndroidGui/GameTitleLoader.h @@ -0,0 +1,48 @@ +#pragma once + +#include "Cafe/IOSU/PDM/iosu_pdm.h" +#include "Cafe/TitleList/TitleId.h" +#include "Cafe/TitleList/TitleList.h" + +struct Game { + std::string name; + uint32 secondsPlayed; + uint16 dlc; + uint16 version; + TitleId titleId; + CafeConsoleRegion region; +}; + +class GameTitleLoader { + std::atomic_bool m_continueLoading = true; + std::mutex m_threadMutex; + std::condition_variable m_condVar; + std::thread m_loaderThread; + std::deque m_titlesToLoad; + std::optional m_callbackIdTitleList; + std::vector m_titleIds; + std::map m_gameInfos; + std::map m_name_cache; + std::function m_onTitleLoaded = nullptr; +public: + GameTitleLoader(); + + void queueTitle(TitleId titleId); + + void setOnTitleLoaded(const std::function &onTitleLoaded); + + void reloadGameTitles(); + + ~GameTitleLoader(); + + void titleRefresh(TitleId titleId); + +private: + void loadGameTitles(); + + std::string GetNameByTitleId(uint64 titleId); + + void registerCallback(); + + void HandleTitleListCallback(CafeTitleListCallbackEvent *evt); +}; diff --git a/src/gui/AndroidGui/Image.cpp b/src/gui/AndroidGui/Image.cpp new file mode 100644 index 00000000..642230a0 --- /dev/null +++ b/src/gui/AndroidGui/Image.cpp @@ -0,0 +1,41 @@ +#include "Image.h" + +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_TGA + +#include "stb_image.h" + +Image::Image(Image &&image) { + this->m_image = image.m_image; + this->m_width = image.m_width; + this->m_height = image.m_height; + this->m_channels = image.m_channels; + image.m_image = nullptr; +} + +Image::Image(const std::vector &imageBytes) { + m_image = + stbi_load_from_memory(imageBytes.data(), imageBytes.size(), &m_width, + &m_height, &m_channels, STBI_rgb_alpha); + if (m_image) { + for (int i = 0; i < m_width * m_height * 4; i += 4) { + uint8 r = m_image[i]; + uint8 g = m_image[i + 1]; + uint8 b = m_image[i + 2]; + uint8 a = m_image[i + 3]; + m_image[i] = b; + m_image[i + 1] = g; + m_image[i + 2] = r; + m_image[i + 3] = a; + } + } +} + +bool Image::isOk() const { return m_image != nullptr; } + +int *Image::intColors() const { return reinterpret_cast(m_image); } + +Image::~Image() { + if (m_image) + stbi_image_free(m_image); +} diff --git a/src/gui/AndroidGui/Image.h b/src/gui/AndroidGui/Image.h new file mode 100644 index 00000000..c2d829ff --- /dev/null +++ b/src/gui/AndroidGui/Image.h @@ -0,0 +1,18 @@ +#pragma once + +struct Image { + uint8_t *m_image = nullptr; + int m_width = 0; + int m_height = 0; + int m_channels = 0; + + Image(Image &&image); + + Image(const std::vector &imageBytes); + + bool isOk() const; + + int *intColors() const; + + ~Image(); +}; diff --git a/src/gui/AndroidGui/OpenGLCanvas.cpp b/src/gui/AndroidGui/OpenGLCanvas.cpp new file mode 100644 index 00000000..e9830b18 --- /dev/null +++ b/src/gui/AndroidGui/OpenGLCanvas.cpp @@ -0,0 +1,3 @@ +bool GLCanvas_MakeCurrent(bool padView) {} +void GLCanvas_SwapBuffers(bool swapTV, bool swapDRC) {} +bool GLCanvas_HasPadViewOpen() {} diff --git a/src/gui/AndroidGui/stb_image.h b/src/gui/AndroidGui/stb_image.h new file mode 100644 index 00000000..d60371b9 --- /dev/null +++ b/src/gui/AndroidGui/stb_image.h @@ -0,0 +1,7897 @@ +/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 7efdda80..d8c0b2ff 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -2,6 +2,11 @@ add_library(CemuGui guiWrapper.h ) +if(ANDROID) + add_subdirectory(AndroidGui) + target_link_libraries(CemuGui PRIVATE CemuAndroidGui) +endif() + if(ENABLE_WXWIDGETS) add_subdirectory(wxGui) target_link_libraries(CemuGui PRIVATE CemuwxGui) diff --git a/src/main.cpp b/src/main.cpp index cf7f7c65..325a5dd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -327,11 +327,12 @@ int main(int argc, char* argv[]) return 0; } +#elif __ANDROID__ #else int main(int argc, char *argv[]) { -#if BOOST_OS_LINUX && !__ANDROID__ +#if BOOST_OS_LINUX XInitThreads(); #endif if (!LaunchSettings::HandleCommandline(argc, argv)) From 785a37e503b140d26cbe2419cf02bb8fcd1a7987 Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Mon, 12 Jun 2023 15:49:48 +0300 Subject: [PATCH 11/30] Add settings, games and emulation activities --- .../Latte/Renderer/Vulkan/VulkanRenderer.cpp | 4 +- src/android/app/build.gradle | 8 +- src/android/app/src/main/AndroidManifest.xml | 13 +- src/android/app/src/main/cpp/CMakeLists.txt | 4 + src/android/app/src/main/cpp/JNIUtils.cpp | 69 +++++++ src/android/app/src/main/cpp/JNIUtils.h | 143 ++++++++++++++ src/android/app/src/main/cpp/native-lib.cpp | 185 +++++++++++++++++- .../java/info/cemu/Cemu/CemuApplication.java | 12 ++ .../info/cemu/Cemu/EmulationActivity.java | 47 +++++ .../info/cemu/Cemu/EmulationFragment.java | 83 ++++++++ .../src/main/java/info/cemu/Cemu/Game.java | 30 +++ .../main/java/info/cemu/Cemu/GameAdapter.java | 88 +++++++++ .../java/info/cemu/Cemu/GamesFragment.java | 56 ++++++ .../java/info/cemu/Cemu/MainActivity.java | 51 +++-- .../java/info/cemu/Cemu/NativeLibrary.java | 40 ++++ .../java/info/cemu/Cemu/SettingsActivity.java | 35 ++++ .../java/info/cemu/Cemu/SettingsFragment.java | 95 +++++++++ .../app/src/main/res/drawable/ic_settings.xml | 10 + .../main/res/layout/activity_emulation.xml | 14 ++ .../app/src/main/res/layout/activity_main.xml | 27 ++- .../src/main/res/layout/activity_settings.xml | 32 +++ .../main/res/layout/fragment_emulation.xml | 13 ++ .../src/main/res/layout/fragment_games.xml | 14 ++ .../src/main/res/layout/fragment_settings.xml | 13 ++ .../app/src/main/res/layout/layout_game.xml | 23 +++ .../src/main/res/layout/settings_checkbox.xml | 35 ++++ .../src/main/res/layout/settings_header.xml | 16 ++ .../src/main/res/layout/settings_spinner.xml | 19 ++ .../app/src/main/res/menu/menu_main.xml | 10 + .../app/src/main/res/navigation/nav_graph.xml | 11 ++ .../res/navigation/nav_settings_graph.xml | 13 ++ .../app/src/main/res/values-land/dimens.xml | 3 + .../app/src/main/res/values-night/themes.xml | 15 +- .../app/src/main/res/values-v23/themes.xml | 9 + .../src/main/res/values-w1240dp/dimens.xml | 3 + .../app/src/main/res/values-w600dp/dimens.xml | 3 + .../app/src/main/res/values/colors.xml | 5 - .../app/src/main/res/values/dimens.xml | 3 + .../app/src/main/res/values/strings.xml | 47 +++++ .../app/src/main/res/values/themes.xml | 17 +- src/config/ActiveSettings.cpp | 4 + src/gui/AndroidGui/AndroidGuiWrapper.cpp | 89 ++++++++- src/gui/AndroidGui/AndroidGuiWrapper.h | 16 ++ src/gui/AndroidGui/CMakeLists.txt | 8 +- src/gui/AndroidGui/GameIconLoader.cpp | 6 +- src/gui/AndroidGui/GameIconLoader.h | 15 +- src/gui/AndroidGui/GameTitleLoader.cpp | 14 +- src/gui/AndroidGui/GameTitleLoader.h | 10 +- src/gui/AndroidGui/Utils.cpp | 80 ++++++++ src/gui/AndroidGui/Utils.h | 6 + 50 files changed, 1480 insertions(+), 86 deletions(-) create mode 100644 src/android/app/src/main/cpp/JNIUtils.cpp create mode 100644 src/android/app/src/main/cpp/JNIUtils.h create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/EmulationActivity.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/Game.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/GameAdapter.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/GamesFragment.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/SettingsActivity.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/SettingsFragment.java create mode 100644 src/android/app/src/main/res/drawable/ic_settings.xml create mode 100644 src/android/app/src/main/res/layout/activity_emulation.xml create mode 100644 src/android/app/src/main/res/layout/activity_settings.xml create mode 100644 src/android/app/src/main/res/layout/fragment_emulation.xml create mode 100644 src/android/app/src/main/res/layout/fragment_games.xml create mode 100644 src/android/app/src/main/res/layout/fragment_settings.xml create mode 100644 src/android/app/src/main/res/layout/layout_game.xml create mode 100644 src/android/app/src/main/res/layout/settings_checkbox.xml create mode 100644 src/android/app/src/main/res/layout/settings_header.xml create mode 100644 src/android/app/src/main/res/layout/settings_spinner.xml create mode 100644 src/android/app/src/main/res/menu/menu_main.xml create mode 100644 src/android/app/src/main/res/navigation/nav_graph.xml create mode 100644 src/android/app/src/main/res/navigation/nav_settings_graph.xml create mode 100644 src/android/app/src/main/res/values-land/dimens.xml create mode 100644 src/android/app/src/main/res/values-v23/themes.xml create mode 100644 src/android/app/src/main/res/values-w1240dp/dimens.xml create mode 100644 src/android/app/src/main/res/values-w600dp/dimens.xml create mode 100644 src/android/app/src/main/res/values/dimens.xml create mode 100644 src/gui/AndroidGui/AndroidGuiWrapper.h create mode 100644 src/gui/AndroidGui/Utils.cpp create mode 100644 src/gui/AndroidGui/Utils.h diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 36ff0331..e8bb7a90 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -49,7 +49,9 @@ const std::vector kOptionalDeviceExtensions = const std::vector kRequiredDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, +#if !__ANDROID__ VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME +#endif }; // Intel doesnt support VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) @@ -442,7 +444,7 @@ VulkanRenderer::VulkanRenderer() deviceFeatures.imageCubeArray = VK_TRUE; #if !BOOST_OS_MACOS deviceFeatures.geometryShader = VK_TRUE; - deviceFeatures.logicOp = VK_TRUE; +// deviceFeatures.logicOp = VK_TRUE; #endif deviceFeatures.occlusionQueryPrecise = VK_TRUE; deviceFeatures.depthClamp = VK_TRUE; diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 3c266dee..1f5257fc 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -13,8 +13,7 @@ android { versionCode 1 versionName "1.0" ndk { -// abiFilters("x86_64", "arm64-v8a") - abiFilters("x86_64") + abiFilters("x86_64", "arm64-v8a") } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -48,7 +47,7 @@ android { '-DENABLE_DISCORD_RPC=OFF', '-DENABLE_WAYLAND=OFF' ) - abiFilters("x86_64") + abiFilters("x86_64", "arm64-v8a") } } } @@ -61,6 +60,9 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' + implementation 'androidx.navigation:navigation-fragment:2.5.3' + implementation 'androidx.navigation:navigation-ui:2.5.3' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 06454137..bb78d539 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -3,18 +3,29 @@ xmlns:tools="http://schemas.android.com/tools"> + + + + android:exported="true" + android:theme="@style/Theme.Cemu"> diff --git a/src/android/app/src/main/cpp/CMakeLists.txt b/src/android/app/src/main/cpp/CMakeLists.txt index accf2fd9..35bf5efa 100644 --- a/src/android/app/src/main/cpp/CMakeLists.txt +++ b/src/android/app/src/main/cpp/CMakeLists.txt @@ -2,11 +2,15 @@ add_library( CemuAndroid SHARED native-lib.cpp + JNIUtils.cpp + JNIUtils.h ) target_link_libraries( CemuAndroid PRIVATE + -landroid CemuCommon + CemuBin CemuAndroidGui ) diff --git a/src/android/app/src/main/cpp/JNIUtils.cpp b/src/android/app/src/main/cpp/JNIUtils.cpp new file mode 100644 index 00000000..073b6493 --- /dev/null +++ b/src/android/app/src/main/cpp/JNIUtils.cpp @@ -0,0 +1,69 @@ +#include "JNIUtils.h" + +namespace JNIUtils +{ +JavaVM *g_jvm = nullptr; +}; + +std::string JNIUtils::JStringToString(JNIEnv *env, jstring jstr) +{ + const char *c_str = env->GetStringUTFChars(jstr, nullptr); + std::string str(c_str); + env->ReleaseStringUTFChars(jstr, c_str); + return str; +} + +jobject JNIUtils::createJavaStringArrayList(JNIEnv *env, const std::vector &strings) +{ + jclass clsArrayList = env->FindClass("java/util/ArrayList"); + jmethodID arrayListConstructor = env->GetMethodID(clsArrayList, "", "()V"); + jobject arrayListObject = env->NewObject(clsArrayList, arrayListConstructor); + jmethodID addMethod = env->GetMethodID(clsArrayList, "add", "(Ljava/lang/Object;)Z"); + env->DeleteLocalRef(clsArrayList); + + for (const auto &string : strings) + { + jstring element = env->NewStringUTF(string.c_str()); + env->CallBooleanMethod(arrayListObject, addMethod, element); + env->DeleteLocalRef(element); + } + return arrayListObject; +} + +JNIUtils::Scopedjobject JNIUtils::getEnumValue(JNIEnv *env, const std::string &enumClassName, const std::string &enumName) +{ + jclass enumClass = env->FindClass(enumClassName.c_str()); + jfieldID fieldID = env->GetStaticFieldID(enumClass, enumName.c_str(), ("L" + enumClassName + ";").c_str()); + jobject enumValue = env->GetStaticObjectField(enumClass, fieldID); + env->DeleteLocalRef(enumClass); + Scopedjobject enumObj = Scopedjobject(enumValue); + env->DeleteLocalRef(enumValue); + return enumObj; +} + +jobject JNIUtils::createArrayList(JNIEnv *env, const std::vector &objects) +{ + static Scopedjclass listClass = Scopedjclass("java/util/ArrayList"); + static jmethodID listConstructor = env->GetMethodID(*listClass, "", "()V"); + static jmethodID listAdd = env->GetMethodID(*listClass, "add", "(Ljava/lang/Object;)Z"); + + jobject arrayList = env->NewObject(*listClass, listConstructor); + for (auto &&obj : objects) + env->CallBooleanMethod(arrayList, listAdd, obj); + return arrayList; +} + +jobject JNIUtils::createJavaStringArrayList(JNIEnv *env, const std::vector &strings) +{ + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + jmethodID arrayListConstructor = env->GetMethodID(arrayListClass, "", "()V"); + jobject arrayListObject = env->NewObject(arrayListClass, arrayListConstructor); + jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + for (const auto &string : strings) + { + jstring element = env->NewString((jchar *)string.c_str(), string.length()); + env->CallBooleanMethod(arrayListObject, addMethod, element); + env->DeleteLocalRef(element); + } + return arrayListObject; +} diff --git a/src/android/app/src/main/cpp/JNIUtils.h b/src/android/app/src/main/cpp/JNIUtils.h new file mode 100644 index 00000000..197ff571 --- /dev/null +++ b/src/android/app/src/main/cpp/JNIUtils.h @@ -0,0 +1,143 @@ +#pragma once + +#include +#include + +namespace JNIUtils +{ +extern JavaVM *g_jvm; + +std::string JStringToString(JNIEnv *env, jstring jstr); + +jobject createJavaStringArrayList(JNIEnv *env, const std::vector &stringList); + +jobject createJavaStringArrayList(JNIEnv *env, const std::vector &stringList); + +class ScopedJNIENV +{ + public: + ScopedJNIENV() + { + jint result = g_jvm->GetEnv((void **)&m_env, JNI_VERSION_1_6); + if (result == JNI_EDETACHED) + { + JavaVMAttachArgs args; + args.version = JNI_VERSION_1_6; + args.name = nullptr; + args.group = nullptr; + result = g_jvm->AttachCurrentThread(&m_env, &args); + if (result == JNI_OK) + m_threadWasAttached = true; + } + } + + JNIEnv *&operator*() { return m_env; } + + JNIEnv *operator->() { return m_env; } + + ~ScopedJNIENV() + { + if (m_threadWasAttached) + g_jvm->DetachCurrentThread(); + } + + private: + JNIEnv *m_env = nullptr; + bool m_threadWasAttached = false; +}; + +class Scopedjobject +{ + public: + Scopedjobject() = default; + + Scopedjobject(Scopedjobject &&other) + { + this->m_jobject = other.m_jobject; + other.m_jobject = nullptr; + } + void deleteRef() + { + if (m_jobject) + { + ScopedJNIENV()->DeleteGlobalRef(m_jobject); + m_jobject = nullptr; + } + } + Scopedjobject &operator=(Scopedjobject &&other) + { + if (this != &other) + { + deleteRef(); + m_jobject = other.m_jobject; + other.m_jobject = nullptr; + } + return *this; + } + jobject &operator*() { return m_jobject; } + + Scopedjobject(jobject obj) + { + if (obj) + m_jobject = ScopedJNIENV()->NewGlobalRef(obj); + } + + ~Scopedjobject() + { + deleteRef(); + } + + bool isValid() const { return m_jobject; } + + private: + jobject m_jobject = nullptr; +}; + +class Scopedjclass +{ + public: + Scopedjclass() = default; + + Scopedjclass(Scopedjclass &&other) + { + this->m_jclass = other.m_jclass; + other.m_jclass = nullptr; + } + + Scopedjclass &operator=(Scopedjclass &&other) + { + if (this != &other) + { + if (m_jclass) + ScopedJNIENV()->DeleteGlobalRef(m_jclass); + m_jclass = other.m_jclass; + other.m_jclass = nullptr; + } + return *this; + } + + Scopedjclass(const std::string &className) + { + ScopedJNIENV scopedEnv; + jobject tempObj = scopedEnv->FindClass(className.c_str()); + m_jclass = static_cast(scopedEnv->NewGlobalRef(tempObj)); + scopedEnv->DeleteLocalRef(tempObj); + } + + ~Scopedjclass() + { + if (m_jclass) + ScopedJNIENV()->DeleteGlobalRef(m_jclass); + } + + bool isValid() const { return m_jclass; } + + jclass &operator*() { return m_jclass; } + + private: + jclass m_jclass = nullptr; +}; + +Scopedjobject getEnumValue(JNIEnv *env, const std::string &enumClassName, const std::string &enumName); +jobject createArrayList(JNIEnv *env, const std::vector &objects); +} // namespace JNIUtils diff --git a/src/android/app/src/main/cpp/native-lib.cpp b/src/android/app/src/main/cpp/native-lib.cpp index 7e5d3f17..1a79da85 100644 --- a/src/android/app/src/main/cpp/native-lib.cpp +++ b/src/android/app/src/main/cpp/native-lib.cpp @@ -1,10 +1,183 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include "AndroidGui/Utils.h" +#include "config/ActiveSettings.h" +#include "config/NetworkSettings.h" +#include "input/InputManager.h" +#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h" -extern "C" JNIEXPORT jstring JNICALL -Java_com_example_myapplication_MainActivity_stringFromJNI( - JNIEnv *env, - jobject /* this */) { - std::string hello = "Hello from C++"; - return env->NewStringUTF(hello.c_str()); + +#include "JNIUtils.h" + +extern "C" +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + JNIUtils::g_jvm = vm; + return JNI_VERSION_1_6; } + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_setSurface(JNIEnv *env, jclass clazz, jobject surface, + jboolean is_main_canvas) { + WindowHandleInfo *windowHandleInfo; + if (is_main_canvas) + windowHandleInfo = &gui_getWindowInfo().canvas_main; + else + windowHandleInfo = &gui_getWindowInfo().canvas_pad; + if (windowHandleInfo->handle) { + ANativeWindow_release(static_cast(windowHandleInfo->handle)); + windowHandleInfo->handle = nullptr; + } + if (surface) + windowHandleInfo->handle = ANativeWindow_fromSurface(env, surface); + gui_getWindowInfo().window_main.handle = windowHandleInfo->handle; +} + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_setSurfaceSize(JNIEnv *env, jclass clazz, jint width, jint height, + jboolean is_main_canvas) { + WindowHandleInfo *windowHandleInfo; + if (is_main_canvas) + windowHandleInfo = &gui_getWindowInfo().canvas_main; + else + windowHandleInfo = &gui_getWindowInfo().canvas_pad; + gui_getWindowInfo().width = width; + gui_getWindowInfo().height = height; +} + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_startGame(JNIEnv *env, jclass clazz, jlong title_id) { + gui_startGame(*reinterpret_cast(&title_id)); +} + +std::shared_ptr g_gameTitleLoadedCallback; + +class AndroidGameTitleLoadedCallback : public GameTitleLoadedCallback { + jmethodID m_onGameTitleLoadedMID; + JNIUtils::Scopedjobject m_gameTitleLoadedCallbackObj; +public: + AndroidGameTitleLoadedCallback(jmethodID onGameTitleLoadedMID, + jobject gameTitleLoadedCallbackObj) + : m_onGameTitleLoadedMID(onGameTitleLoadedMID), + m_gameTitleLoadedCallbackObj(gameTitleLoadedCallbackObj) {} + + void onTitleLoaded(const Game &game) override { + JNIUtils::ScopedJNIENV env; + jstring name = env->NewStringUTF(game.name.c_str()); + jlong titleId = *reinterpret_cast(&game.titleId); + env->CallVoidMethod(*m_gameTitleLoadedCallbackObj, m_onGameTitleLoadedMID, titleId, name); + env->DeleteLocalRef(name); + } +}; + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_setGameTitleLoadedCallback(JNIEnv *env, jclass clazz, + jobject game_title_loaded_callback) { + jclass gameTitleLoadedCallbackClass = env->GetObjectClass(game_title_loaded_callback); + jmethodID onGameTitleLoadedMID = env->GetMethodID(gameTitleLoadedCallbackClass, + "onGameTitleLoaded", + "(JLjava/lang/String;)V"); + env->DeleteLocalRef(gameTitleLoadedCallbackClass); + g_gameTitleLoadedCallback = std::make_shared( + onGameTitleLoadedMID, + game_title_loaded_callback); + gui_setOnGameTitleLoaded(g_gameTitleLoadedCallback); +} + +std::shared_ptr g_gameIconLoadedCallback; + +class AndroidGameIconLoadedCallback : public GameIconLoadedCallback { + jmethodID m_onGameIconLoadedMID; + JNIUtils::Scopedjobject m_gameTitleLoadedCallbackObj; +public: + AndroidGameIconLoadedCallback(jmethodID onGameIconLoadedMID, + jobject gameTitleLoadedCallbackObj) + : m_onGameIconLoadedMID(onGameIconLoadedMID), + m_gameTitleLoadedCallbackObj(gameTitleLoadedCallbackObj) {} + + void onIconLoaded(TitleId titleId, const Image &iconData) override { + JNIUtils::ScopedJNIENV env; + jlong jTitleId = *reinterpret_cast(&titleId); + jintArray jIconData = env->NewIntArray(iconData.m_width * iconData.m_height); + env->SetIntArrayRegion(jIconData, 0, iconData.m_width * iconData.m_height, + reinterpret_cast(iconData.m_image)); + env->CallVoidMethod(*m_gameTitleLoadedCallbackObj, m_onGameIconLoadedMID, jTitleId, + jIconData, iconData.m_width, iconData.m_height); + } +}; + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_setGameIconLoadedCallback(JNIEnv *env, jclass clazz, + jobject game_icon_loaded_callback) { + jclass gameIconLoadedCallbackClass = env->GetObjectClass(game_icon_loaded_callback); + jmethodID gameIconLoadedMID = env->GetMethodID(gameIconLoadedCallbackClass, + "onGameIconLoaded", + "(J[III)V"); + env->DeleteLocalRef(gameIconLoadedCallbackClass); + g_gameIconLoadedCallback = std::make_shared(gameIconLoadedMID, + game_icon_loaded_callback); + gui_setOnGameIconLoaded(g_gameIconLoadedCallback); +} + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_requestGameIcon(JNIEnv *env, jclass clazz, jlong title_id) { + TitleId titleId = *reinterpret_cast(&title_id); + gui_requestGameIcon(titleId); +} + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_reloadGameTitles(JNIEnv *env, jclass clazz) { + gui_reloadGameTitles(); +} +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_initializeActiveSettings(JNIEnv *env, jclass clazz, + jstring data_path, jstring cache_path) { + std::string dataPath = JNIUtils::JStringToString(env, data_path); + std::string cachePath = JNIUtils::JStringToString(env, cache_path); + ActiveSettings::LoadOnce({}, dataPath, dataPath, cachePath, dataPath); +} + +int mainEmulatorHLE(); + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_initializeEmulation(JNIEnv *env, jclass clazz) { + NetworkConfig::LoadOnce(); + mainEmulatorHLE(); + InputManager::instance().load(); + InitializeGlobalVulkan(); + createCemuDirectories(); +} +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_initializerRenderer(JNIEnv *env, jclass clazz) { + g_renderer = std::make_unique(); +} +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_initializeRendererSurface(JNIEnv *env, jclass clazz, + jboolean is_main_canvas) { + int width, height; + if (is_main_canvas) + gui_getWindowPhysSize(width, height); + else + gui_getPadWindowPhysSize(width, height); + VulkanRenderer::GetInstance()->InitializeSurface( + {width, height}, + is_main_canvas); + +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java b/src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java new file mode 100644 index 00000000..98c37110 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java @@ -0,0 +1,12 @@ +package info.cemu.Cemu; + +import android.app.Application; + +public class CemuApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + NativeLibrary.initializeActiveSettings(getExternalFilesDir(null).getAbsoluteFile().toString(), getCacheDir().toString()); + NativeLibrary.initializeEmulation(); + } +} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/EmulationActivity.java b/src/android/app/src/main/java/info/cemu/Cemu/EmulationActivity.java new file mode 100644 index 00000000..16f878e6 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/EmulationActivity.java @@ -0,0 +1,47 @@ +package info.cemu.Cemu; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsAnimationCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.view.WindowInsetsControllerCompat; + +import android.os.Bundle; + +import info.cemu.Cemu.databinding.ActivityEmulationBinding; +import info.cemu.Cemu.databinding.ActivityMainBinding; + +public class EmulationActivity extends AppCompatActivity { + + public static final String GAME_TITLE_ID = "GameTitleId"; + long gameTitleId; + ActivityEmulationBinding binding; + EmulationFragment emulationFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle extras = getIntent().getExtras(); + if (extras != null) { + gameTitleId = extras.getLong(GAME_TITLE_ID); + } + setFullscreen(); + binding = ActivityEmulationBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + emulationFragment = (EmulationFragment) getSupportFragmentManager().findFragmentById(R.id.emulation_frame); + if (emulationFragment == null) { + emulationFragment = EmulationFragment.newInstance(gameTitleId); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.emulation_frame, emulationFragment) + .commit(); + } + } + + void setFullscreen() { + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + WindowInsetsControllerCompat controller = new WindowInsetsControllerCompat(getWindow(), getWindow().getDecorView()); + controller.hide(WindowInsetsCompat.Type.systemBars()); + controller.setSystemBarsBehavior(WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java new file mode 100644 index 00000000..93355862 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java @@ -0,0 +1,83 @@ +package info.cemu.Cemu; + +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import android.view.LayoutInflater; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; + +import java.util.Objects; + +import info.cemu.Cemu.databinding.FragmentEmulationBinding; +import info.cemu.Cemu.databinding.FragmentGamesBinding; + +public class EmulationFragment extends Fragment implements SurfaceHolder.Callback { + FragmentEmulationBinding binding; + + public EmulationFragment() { + } + + static EmulationFragment newInstance(long gameTitleId) { + EmulationFragment emulationFragment = new EmulationFragment(); + Bundle arguments = new Bundle(); + arguments.putLong(EmulationActivity.GAME_TITLE_ID, gameTitleId); + emulationFragment.setArguments(arguments); + return emulationFragment; + } + + long gameTitleId; + boolean isRunning; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle arguments = getArguments(); + if (arguments != null) { + gameTitleId = arguments.getLong(EmulationActivity.GAME_TITLE_ID); + } + + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentEmulationBinding.inflate(inflater, container, false); + SurfaceView mainCanvas = binding.mainCanvas; + mainCanvas.getHolder().addCallback(this); + return binding.getRoot(); + } + + private void startGame() { + if (!isRunning) { + isRunning = true; + NativeLibrary.initializerRenderer(); + NativeLibrary.initializeRendererSurface(true); + NativeLibrary.startGame(gameTitleId); + } + } + + @Override + public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { + + } + + @Override + public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int format, int width, int height) { + NativeLibrary.setSurface(surfaceHolder.getSurface(), true); + NativeLibrary.setSurfaceSize(width, height, true); + startGame(); + } + + @Override + public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) { + + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/Game.java b/src/android/app/src/main/java/info/cemu/Cemu/Game.java new file mode 100644 index 00000000..68bda1f8 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/Game.java @@ -0,0 +1,30 @@ +package info.cemu.Cemu; + +import android.graphics.Bitmap; + +public class Game { + Long titleId; + String title; + Bitmap icon; + + public Game(Long titleId, String title) { + this.titleId = titleId; + this.title = title; + } + + public void setIconData(int[] colors, int width, int height) { + icon = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888); + } + + public Bitmap getIcon() { + return icon; + } + + public Long getTitleId() { + return titleId; + } + + public String getTitle() { + return title; + } +} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/GameAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/GameAdapter.java new file mode 100644 index 00000000..7df4a747 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/GameAdapter.java @@ -0,0 +1,88 @@ +package info.cemu.Cemu; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + + +import java.util.HashMap; +import java.util.Vector; + +public class GameAdapter extends RecyclerView.Adapter { + HashMap gameInfoMap; + Vector gameTitleIds; + private final GameTitleClickAction gameTitleClickAction; + + public interface GameTitleClickAction { + void action(long titleId); + } + + public GameAdapter(GameTitleClickAction gameTitleClickAction) { + super(); + this.gameTitleClickAction = gameTitleClickAction; + gameTitleIds = new Vector<>(); + gameInfoMap = new HashMap<>(); + } + + @NonNull + @Override + public GameAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_game, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull GameAdapter.ViewHolder holder, int position) { + Game game = gameInfoMap.get(gameTitleIds.get(position)); + if (game != null) { + holder.icon.setImageBitmap(game.getIcon()); + holder.text.setText(game.getTitle()); + holder.itemView.setOnClickListener(v -> { + long titleId = game.titleId; + gameTitleClickAction.action(titleId); + }); + } + } + + @Override + public int getItemCount() { + return gameTitleIds.size(); + } + + void clear() { + int itemCount = gameTitleIds.size(); + notifyItemRangeRemoved(0, itemCount); + gameInfoMap.clear(); + gameTitleIds.clear(); + } + + void addGameInfo(long titleId, String title) { + gameTitleIds.addElement(titleId); + gameInfoMap.put(titleId, new Game(titleId, title)); + notifyItemInserted(gameTitleIds.size() - 1); + } + + void setGameIcon(long titleId, int[] colors, int width, int height) { + Game game = gameInfoMap.get(titleId); + if (game != null) { + game.setIconData(colors, width, height); + notifyItemChanged(gameTitleIds.indexOf(titleId)); + } + } + + public static class ViewHolder extends RecyclerView.ViewHolder { + ImageView icon; + TextView text; + + public ViewHolder(View itemView) { + super(itemView); + icon = itemView.findViewById(R.id.game_icon); + text = itemView.findViewById(R.id.game_title); + } + } +} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/GamesFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/GamesFragment.java new file mode 100644 index 00000000..29caedd1 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/GamesFragment.java @@ -0,0 +1,56 @@ +package info.cemu.Cemu; + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import info.cemu.Cemu.databinding.FragmentGamesBinding; + +public class GamesFragment extends Fragment { + FragmentGamesBinding binding; + GameAdapter gameAdapter; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentGamesBinding.inflate(inflater, container, false); + RecyclerView recyclerView = binding.gamesRecyclerView; + recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); + View rootView = binding.getRoot(); + gameAdapter = new GameAdapter(titleId -> { + Intent intent = new Intent(getContext(), EmulationActivity.class); + intent.putExtra(EmulationActivity.GAME_TITLE_ID, titleId); + startActivity(intent); + }); + binding.gamesSwipeRefresh.setOnRefreshListener(() -> { + gameAdapter.clear(); + NativeLibrary.reloadGameTitles(); + binding.gamesSwipeRefresh.setRefreshing(false); + }); + recyclerView.setAdapter(gameAdapter); + NativeLibrary.setGameTitleLoadedCallback((titleId, title) -> { + requireActivity().runOnUiThread(() -> { + gameAdapter.addGameInfo(titleId, title); + NativeLibrary.requestGameIcon(titleId); + }); + }); + NativeLibrary.setGameIconLoadedCallback((titleId, colors, width, height) -> { + requireActivity().runOnUiThread(() -> gameAdapter.setGameIcon(titleId, colors, width, height)); + }); + NativeLibrary.reloadGameTitles(); + return rootView; + } + +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java b/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java index 0764945c..37f6ef1c 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/MainActivity.java @@ -1,35 +1,54 @@ package info.cemu.Cemu; +import android.content.Intent; +import android.os.Bundle; + +import com.google.android.material.snackbar.Snackbar; + import androidx.appcompat.app.AppCompatActivity; -import android.os.Bundle; -import android.widget.TextView; +import android.view.View; + +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.ui.AppBarConfiguration; +import androidx.navigation.ui.NavigationUI; import info.cemu.Cemu.databinding.ActivityMainBinding; +import android.view.Menu; +import android.view.MenuItem; + + public class MainActivity extends AppCompatActivity { - - static { - System.loadLibrary("CemuAndroid"); - } - private ActivityMainBinding binding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - // Example of a call to a native method - TextView tv = binding.sampleText; - tv.setText(stringFromJNI()); + binding.fab.setOnClickListener( + view -> { + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + } + ); } - /** - * A native method that is implemented by the 'Cemu' native library, - * which is packaged with this application. - */ - public native String stringFromJNI(); + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } } \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java b/src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java new file mode 100644 index 00000000..44dd98a8 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java @@ -0,0 +1,40 @@ +package info.cemu.Cemu; + +import android.view.Surface; + +public class NativeLibrary { + static { + System.loadLibrary("CemuAndroid"); + } + + public static native void setSurface(Surface surface, boolean isMainCanvas); + + public static native void setSurfaceSize(int width, int height, boolean isMainCanvas); + + public static native void initializerRenderer(); + + public static native void initializeRendererSurface(boolean isMainCanvas); + + public static native void startGame(long titleId); + + + public interface GameTitleLoadedCallback { + void onGameTitleLoaded(long titleId, String title); + } + + public static native void setGameTitleLoadedCallback(GameTitleLoadedCallback gameTitleLoadedCallback); + + public interface GameIconLoadedCallback { + void onGameIconLoaded(long titleId, int[] colors, int width, int height); + } + + public static native void setGameIconLoadedCallback(GameIconLoadedCallback gameIconLoadedCallback); + + public static native void requestGameIcon(long titleId); + + public static native void reloadGameTitles(); + + public static native void initializeActiveSettings(String dataPath, String cachePath); + + public static native void initializeEmulation(); +} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/SettingsActivity.java b/src/android/app/src/main/java/info/cemu/Cemu/SettingsActivity.java new file mode 100644 index 00000000..2c935d9d --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/SettingsActivity.java @@ -0,0 +1,35 @@ +package info.cemu.Cemu; + +import androidx.appcompat.app.AppCompatActivity; + +import android.content.Intent; +import android.os.Bundle; + +import java.util.Objects; + +import info.cemu.Cemu.databinding.ActivitySettingsBinding; + +public class SettingsActivity extends AppCompatActivity { + + private ActivitySettingsBinding binding; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivitySettingsBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + setSupportActionBar(binding.toolbarSettings); + Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); + } + + @Override + public boolean onSupportNavigateUp() { + if (getSupportFragmentManager().getBackStackEntryCount() > 0) { + getSupportFragmentManager().popBackStack(); + } else { + finish(); + } + return true; + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/SettingsFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/SettingsFragment.java new file mode 100644 index 00000000..e9bf7043 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/SettingsFragment.java @@ -0,0 +1,95 @@ +package info.cemu.Cemu; + +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.SpinnerAdapter; + +import java.util.ArrayList; +import java.util.List; + +import info.cemu.Cemu.databinding.FragmentSettingsBinding; + +import info.cemu.Cemu.databinding.SettingsCheckboxBinding; +import info.cemu.Cemu.databinding.SettingsHeaderBinding; +import info.cemu.Cemu.databinding.SettingsCheckboxBinding; +import info.cemu.Cemu.databinding.SettingsSpinnerBinding; + +public class SettingsFragment extends Fragment { + protected FragmentSettingsBinding binding; + + public SettingsFragment() { + } + +// public abstract int getSettingsNameId(); + + protected interface OnCheckBoxStateListener { + void onStateChanged(boolean checked); + } + + protected interface OnItemSelectedPositionListener { + void onItemSelected(int position); + } + + protected void addCheckBox(String title, String description, boolean initialValue, OnCheckBoxStateListener checkBoxStateListener) { + View checkboxLayout = getLayoutInflater().inflate(R.layout.settings_spinner, null); + binding.settingsLayout.addView(checkboxLayout); + SettingsCheckboxBinding checkboxBinding = SettingsCheckboxBinding.bind(checkboxLayout); + checkboxBinding.textviewCheckboxName.setText(title); + checkboxBinding.checkbox.setChecked(initialValue); + checkboxBinding.textviewCheckboxDescription.setText(description); + checkboxLayout.setOnClickListener(v -> { + boolean isChecked = !checkboxBinding.checkbox.isChecked(); + checkboxBinding.checkbox.setChecked(isChecked); + checkBoxStateListener.onStateChanged(isChecked); + }); + } + +// protected void addSeparator() { +// View divider = getLayoutInflater().inflate(R.layout.divider_layout, null); +// binding.settingsLayout.addView(divider); +// } + + protected void addHeader(String title) { + View headerLayout = getLayoutInflater().inflate(R.layout.settings_header, null); + binding.settingsLayout.addView(headerLayout); + SettingsHeaderBinding headerBinding = SettingsHeaderBinding.bind(headerLayout); + headerBinding.textviewHeaderName.setText(title); + } + + protected void addSpinner(String title, List items, int initialPosition, OnItemSelectedPositionListener itemSelectedPositionListener) { + View spinnerLayout = getLayoutInflater().inflate(R.layout.settings_spinner, null); + binding.settingsLayout.addView(spinnerLayout); + SettingsSpinnerBinding spinnerBinding = SettingsSpinnerBinding.bind(spinnerLayout); + spinnerBinding.textviewSpinnerName.setText(title); + spinnerLayout.setOnClickListener(v -> spinnerBinding.spinner.performClick()); + ArrayAdapter adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, items); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerBinding.spinner.setAdapter(adapter); + spinnerBinding.spinner.setSelection(initialPosition); + spinnerBinding.spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + itemSelectedPositionListener.onItemSelected(position); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentSettingsBinding.inflate(inflater, container, false); + return binding.getRoot(); + } +} \ No newline at end of file diff --git a/src/android/app/src/main/res/drawable/ic_settings.xml b/src/android/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 00000000..f112c1d1 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/android/app/src/main/res/layout/activity_emulation.xml b/src/android/app/src/main/res/layout/activity_emulation.xml new file mode 100644 index 00000000..7d630bff --- /dev/null +++ b/src/android/app/src/main/res/layout/activity_emulation.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/android/app/src/main/res/layout/activity_main.xml b/src/android/app/src/main/res/layout/activity_main.xml index 0fdf9852..0e5dc3c2 100644 --- a/src/android/app/src/main/res/layout/activity_main.xml +++ b/src/android/app/src/main/res/layout/activity_main.xml @@ -1,19 +1,28 @@ - - + + + android:layout_gravity="bottom|end" + android:layout_marginEnd="@dimen/settings_fab_margin" + android:layout_marginBottom="16dp" + android:contentDescription="@string/settings" + app:srcCompat="@drawable/ic_settings" /> - \ No newline at end of file + \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 00000000..81b281d7 --- /dev/null +++ b/src/android/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/fragment_emulation.xml b/src/android/app/src/main/res/layout/fragment_emulation.xml new file mode 100644 index 00000000..ca69352a --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_emulation.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/fragment_games.xml b/src/android/app/src/main/res/layout/fragment_games.xml new file mode 100644 index 00000000..d80fcb10 --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_games.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/android/app/src/main/res/layout/fragment_settings.xml b/src/android/app/src/main/res/layout/fragment_settings.xml new file mode 100644 index 00000000..6c77450e --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_settings.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/src/android/app/src/main/res/layout/layout_game.xml b/src/android/app/src/main/res/layout/layout_game.xml new file mode 100644 index 00000000..e1125c09 --- /dev/null +++ b/src/android/app/src/main/res/layout/layout_game.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/src/android/app/src/main/res/layout/settings_checkbox.xml b/src/android/app/src/main/res/layout/settings_checkbox.xml new file mode 100644 index 00000000..a3fe1c3a --- /dev/null +++ b/src/android/app/src/main/res/layout/settings_checkbox.xml @@ -0,0 +1,35 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/settings_header.xml b/src/android/app/src/main/res/layout/settings_header.xml new file mode 100644 index 00000000..b541bfc4 --- /dev/null +++ b/src/android/app/src/main/res/layout/settings_header.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/android/app/src/main/res/layout/settings_spinner.xml b/src/android/app/src/main/res/layout/settings_spinner.xml new file mode 100644 index 00000000..86954226 --- /dev/null +++ b/src/android/app/src/main/res/layout/settings_spinner.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/src/android/app/src/main/res/menu/menu_main.xml b/src/android/app/src/main/res/menu/menu_main.xml new file mode 100644 index 00000000..076de2d3 --- /dev/null +++ b/src/android/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/navigation/nav_graph.xml b/src/android/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 00000000..843e307c --- /dev/null +++ b/src/android/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/navigation/nav_settings_graph.xml b/src/android/app/src/main/res/navigation/nav_settings_graph.xml new file mode 100644 index 00000000..e7197238 --- /dev/null +++ b/src/android/app/src/main/res/navigation/nav_settings_graph.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/values-land/dimens.xml b/src/android/app/src/main/res/values-land/dimens.xml new file mode 100644 index 00000000..65c04647 --- /dev/null +++ b/src/android/app/src/main/res/values-land/dimens.xml @@ -0,0 +1,3 @@ + + 48dp + \ No newline at end of file diff --git a/src/android/app/src/main/res/values-night/themes.xml b/src/android/app/src/main/res/values-night/themes.xml index c0c8b990..d25b0f2f 100644 --- a/src/android/app/src/main/res/values-night/themes.xml +++ b/src/android/app/src/main/res/values-night/themes.xml @@ -1,16 +1,7 @@ - \ No newline at end of file diff --git a/src/android/app/src/main/res/values-v23/themes.xml b/src/android/app/src/main/res/values-v23/themes.xml new file mode 100644 index 00000000..17852cdf --- /dev/null +++ b/src/android/app/src/main/res/values-v23/themes.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/android/app/src/main/res/values-w1240dp/dimens.xml b/src/android/app/src/main/res/values-w1240dp/dimens.xml new file mode 100644 index 00000000..63f5808e --- /dev/null +++ b/src/android/app/src/main/res/values-w1240dp/dimens.xml @@ -0,0 +1,3 @@ + + 200dp + \ No newline at end of file diff --git a/src/android/app/src/main/res/values-w600dp/dimens.xml b/src/android/app/src/main/res/values-w600dp/dimens.xml new file mode 100644 index 00000000..65c04647 --- /dev/null +++ b/src/android/app/src/main/res/values-w600dp/dimens.xml @@ -0,0 +1,3 @@ + + 48dp + \ No newline at end of file diff --git a/src/android/app/src/main/res/values/colors.xml b/src/android/app/src/main/res/values/colors.xml index f8c6127d..c8524cd9 100644 --- a/src/android/app/src/main/res/values/colors.xml +++ b/src/android/app/src/main/res/values/colors.xml @@ -1,10 +1,5 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 #FF000000 #FFFFFFFF \ No newline at end of file diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000..0bb1f4f2 --- /dev/null +++ b/src/android/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + \ No newline at end of file diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 24871da5..57b78d8a 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1,3 +1,50 @@ Cemu + Settings + + First Fragment + Second Fragment + Next + Previous + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris + volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus + dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad + litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend + diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, + ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n + Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus + egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed + neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada + fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, + molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor + bibendum, vel congue leo egestas.\n\n + Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit + amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, + molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer + interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at + lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, + in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque + est.\n\n + Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. + Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui + non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In + eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, + quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra + ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a + placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus + convallis.\n\n + Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et + malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa + gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, + libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper + sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus + libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus + vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim. + + Settings + + Hello blank fragment + Game Icon \ No newline at end of file diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml index 8209cef4..2d664e10 100644 --- a/src/android/app/src/main/res/values/themes.xml +++ b/src/android/app/src/main/res/values/themes.xml @@ -1,16 +1,9 @@ - + + \ No newline at end of file diff --git a/src/android/app/src/main/res/values/refs.xml b/src/android/app/src/main/res/values/refs.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/src/android/app/src/main/res/values/refs.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 57b78d8a..bd652bfa 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -1,50 +1,62 @@ Cemu Settings - - First Fragment - Second Fragment - Next - Previous - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris - volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus - dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad - litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend - diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a, - ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n - Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus - egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed - neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada - fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae, - molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor - bibendum, vel congue leo egestas.\n\n - Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit - amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel, - molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer - interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at - lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula, - in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque - est.\n\n - Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh. - Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui - non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In - eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc, - quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra - ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a - placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus - convallis.\n\n - Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et - malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa - gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper, - libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper - sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus - libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus - vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim. - Settings - - Hello blank fragment Game Icon + Select controller + Controller\'s settings + Emulated controller + Clear + Disabled + Wii U GamePad + Wii U Pro Controller + Wiimote + Wii U Classic Controller + A + B + X + Y + L + R + ZL + ZR + Plus + Minus + Up + Down + Left + Right + Click + Up + Down + Left + Right + Click + Up + Down + Left + Right + Blow Mic + Show Screen + Home + 1 + 2 + Z + C + Up + Down + Left + Right + Input binding + Trigger an input to bind it to %1$s + Clear + Cancel + Controller %1d + Input Settings + Emulated controller: + Emulated controller: %1s + Are you sure you want to exit? + Exit Confirmation + Yes + No \ No newline at end of file diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml index 2d664e10..4356ebeb 100644 --- a/src/android/app/src/main/res/values/themes.xml +++ b/src/android/app/src/main/res/values/themes.xml @@ -1,8 +1,8 @@ - -