diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 07249e650..5cf78d8c2 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -19,8 +19,6 @@ add_library(common
   fifo_queue.h
   file_system.cpp
   file_system.h
-  image.cpp
-  image.h
   intrin.h
   hash_combine.h
   heap_array.h
@@ -63,7 +61,7 @@ add_library(common
 target_include_directories(common PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
 target_include_directories(common PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
 target_link_libraries(common PUBLIC fmt Threads::Threads fast_float)
-target_link_libraries(common PRIVATE stb ZLIB::ZLIB minizip "${CMAKE_DL_LIBS}")
+target_link_libraries(common PRIVATE ZLIB::ZLIB minizip "${CMAKE_DL_LIBS}")
 
 if(WIN32)
   target_sources(common PRIVATE
diff --git a/src/common/common.vcxproj b/src/common/common.vcxproj
index a6663a857..1fdd6177d 100644
--- a/src/common/common.vcxproj
+++ b/src/common/common.vcxproj
@@ -18,7 +18,6 @@
     <ClInclude Include="file_system.h" />
     <ClInclude Include="hash_combine.h" />
     <ClInclude Include="heap_array.h" />
-    <ClInclude Include="image.h" />
     <ClInclude Include="intrin.h" />
     <ClInclude Include="layered_settings_interface.h" />
     <ClInclude Include="log.h" />
@@ -52,7 +51,6 @@
     <ClCompile Include="error.cpp" />
     <ClCompile Include="fastjmp.cpp" />
     <ClCompile Include="file_system.cpp" />
-    <ClCompile Include="image.cpp" />
     <ClCompile Include="layered_settings_interface.cpp" />
     <ClCompile Include="log.cpp" />
     <ClCompile Include="memmap.cpp" />
@@ -92,9 +90,6 @@
     <ProjectReference Include="..\..\dep\minizip\minizip.vcxproj">
       <Project>{8bda439c-6358-45fb-9994-2ff083babe06}</Project>
     </ProjectReference>
-    <ProjectReference Include="..\..\dep\stb\stb.vcxproj">
-      <Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
-    </ProjectReference>
     <ProjectReference Include="..\..\dep\zlib\zlib.vcxproj">
       <Project>{7ff9fdb9-d504-47db-a16a-b08071999620}</Project>
     </ProjectReference>
diff --git a/src/common/common.vcxproj.filters b/src/common/common.vcxproj.filters
index 1e972ee0d..a093a195c 100644
--- a/src/common/common.vcxproj.filters
+++ b/src/common/common.vcxproj.filters
@@ -19,7 +19,6 @@
     <ClInclude Include="progress_callback.h" />
     <ClInclude Include="bitutils.h" />
     <ClInclude Include="dimensional_array.h" />
-    <ClInclude Include="image.h" />
     <ClInclude Include="minizip_helpers.h" />
     <ClInclude Include="thirdparty\StackWalker.h">
       <Filter>thirdparty</Filter>
@@ -57,7 +56,6 @@
     <ClCompile Include="string_util.cpp" />
     <ClCompile Include="md5_digest.cpp" />
     <ClCompile Include="progress_callback.cpp" />
-    <ClCompile Include="image.cpp" />
     <ClCompile Include="minizip_helpers.cpp" />
     <ClCompile Include="thirdparty\StackWalker.cpp">
       <Filter>thirdparty</Filter>
diff --git a/src/core/controller.h b/src/core/controller.h
index 701e4a62f..29723e408 100644
--- a/src/core/controller.h
+++ b/src/core/controller.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 
 #pragma once
@@ -7,8 +7,6 @@
 #include "settings.h"
 #include "types.h"
 
-#include "common/image.h"
-
 #include <memory>
 #include <optional>
 #include <span>
diff --git a/src/core/texture_replacements.cpp b/src/core/texture_replacements.cpp
index c41469bdb..153698c4e 100644
--- a/src/core/texture_replacements.cpp
+++ b/src/core/texture_replacements.cpp
@@ -90,7 +90,7 @@ void TextureReplacements::DumpVRAMWrite(u32 width, u32 height, const void* pixel
   if (filename.empty())
     return;
 
-  Common::RGBA8Image image;
+  RGBA8Image image;
   image.SetSize(width, height);
 
   const u16* src_pixels = reinterpret_cast<const u16*>(pixels);
@@ -274,7 +274,7 @@ const TextureReplacementTexture* TextureReplacements::LoadTexture(const std::str
   if (it != m_texture_cache.end())
     return &it->second;
 
-  Common::RGBA8Image image;
+  RGBA8Image image;
   if (!image.LoadFromFile(filename.c_str()))
   {
     Log_ErrorPrintf("Failed to load '%s'", filename.c_str());
diff --git a/src/core/texture_replacements.h b/src/core/texture_replacements.h
index ae67e93ab..f713f58f2 100644
--- a/src/core/texture_replacements.h
+++ b/src/core/texture_replacements.h
@@ -1,10 +1,14 @@
-// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 
 #pragma once
+
+#include "util/image.h"
+
 #include "common/hash_combine.h"
-#include "common/image.h"
+
 #include "types.h"
+
 #include <string>
 #include <tuple>
 #include <unordered_map>
@@ -36,7 +40,7 @@ struct hash<TextureReplacementHash>
 };
 } // namespace std
 
-using TextureReplacementTexture = Common::RGBA8Image;
+using TextureReplacementTexture = RGBA8Image;
 
 class TextureReplacements
 {
diff --git a/src/duckstation-qt/memorycardeditorwindow.cpp b/src/duckstation-qt/memorycardeditorwindow.cpp
index 15ebe6a6c..88a8f21a5 100644
--- a/src/duckstation-qt/memorycardeditorwindow.cpp
+++ b/src/duckstation-qt/memorycardeditorwindow.cpp
@@ -1,13 +1,17 @@
-// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 
 #include "memorycardeditorwindow.h"
+#include "qtutils.h"
+
+#include "core/host.h"
+#include "core/settings.h"
+
+#include "common/assert.h"
 #include "common/file_system.h"
 #include "common/path.h"
 #include "common/string_util.h"
-#include "core/host.h"
-#include "core/settings.h"
-#include "qtutils.h"
+
 #include <QtCore/QFileInfo>
 #include <QtWidgets/QFileDialog>
 #include <QtWidgets/QMessageBox>
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index 57e0c18d3..4296f4c4d 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -33,6 +33,8 @@ add_library(util
   host.h
   http_downloader.cpp
   http_downloader.h
+  image.cpp
+  image.h
   imgui_fullscreen.cpp
   imgui_fullscreen.h
   imgui_manager.cpp
diff --git a/src/common/image.cpp b/src/util/image.cpp
similarity index 97%
rename from src/common/image.cpp
rename to src/util/image.cpp
index 674fe1599..7a6be78aa 100644
--- a/src/common/image.cpp
+++ b/src/util/image.cpp
@@ -1,19 +1,20 @@
-// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 
 #include "image.h"
-#include "byte_stream.h"
-#include "file_system.h"
-#include "log.h"
-#include "path.h"
-#include "scoped_guard.h"
+
+#include "common/byte_stream.h"
+#include "common/file_system.h"
+#include "common/log.h"
+#include "common/path.h"
+#include "common/scoped_guard.h"
+#include "common/string_util.h"
+
 #include "stb_image.h"
 #include "stb_image_resize.h"
 #include "stb_image_write.h"
-#include "string_util.h"
-Log_SetChannel(Image);
 
-using namespace Common;
+Log_SetChannel(Image);
 
 #if 0
 static bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size);
@@ -68,11 +69,25 @@ static const FormatHandler* GetFormatHandler(const std::string_view& extension)
 
 RGBA8Image::RGBA8Image() = default;
 
-RGBA8Image::RGBA8Image(const RGBA8Image& copy) : Image(copy) {}
+RGBA8Image::RGBA8Image(const RGBA8Image& copy) : Image(copy)
+{
+}
 
-RGBA8Image::RGBA8Image(u32 width, u32 height, const u32* pixels) : Image(width, height, pixels) {}
+RGBA8Image::RGBA8Image(u32 width, u32 height, const u32* pixels) : Image(width, height, pixels)
+{
+}
 
-RGBA8Image::RGBA8Image(RGBA8Image&& move) : Image(move) {}
+RGBA8Image::RGBA8Image(RGBA8Image&& move) : Image(move)
+{
+}
+
+RGBA8Image::RGBA8Image(u32 width, u32 height) : Image(width, height)
+{
+}
+
+RGBA8Image::RGBA8Image(u32 width, u32 height, std::vector<u32> pixels) : Image(width, height, std::move(pixels))
+{
+}
 
 RGBA8Image& RGBA8Image::operator=(const RGBA8Image& copy)
 {
diff --git a/src/common/image.h b/src/util/image.h
similarity index 90%
rename from src/common/image.h
rename to src/util/image.h
index c7c02d188..232e3f034 100644
--- a/src/common/image.h
+++ b/src/util/image.h
@@ -1,22 +1,25 @@
-// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 
 #pragma once
-#include "assert.h"
-#include "types.h"
+
+#include "common/assert.h"
+#include "common/types.h"
+
 #include <algorithm>
 #include <cstdio>
 #include <cstring>
 #include <optional>
 #include <vector>
 
-namespace Common {
 template<typename PixelType>
 class Image
 {
 public:
   Image() = default;
+  Image(u32 width, u32 height) { SetSize(width, height); }
   Image(u32 width, u32 height, const PixelType* pixels) { SetPixels(width, height, pixels); }
+  Image(u32 width, u32 height, std::vector<PixelType> pixels) { SetPixels(width, height, std::move(pixels)); }
   Image(const Image& copy)
   {
     m_width = copy.m_width;
@@ -114,7 +117,9 @@ public:
   static constexpr int DEFAULT_SAVE_QUALITY = 85;
 
   RGBA8Image();
+  RGBA8Image(u32 width, u32 height);
   RGBA8Image(u32 width, u32 height, const u32* pixels);
+  RGBA8Image(u32 width, u32 height, std::vector<u32> pixels);
   RGBA8Image(const RGBA8Image& copy);
   RGBA8Image(RGBA8Image&& move);
 
@@ -132,5 +137,3 @@ public:
   void Resize(u32 new_width, u32 new_height);
   void Resize(const RGBA8Image* src_image, u32 new_width, u32 new_height);
 };
-
-} // namespace Common
diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp
index 3632c1b95..6efa48df6 100644
--- a/src/util/imgui_fullscreen.cpp
+++ b/src/util/imgui_fullscreen.cpp
@@ -5,12 +5,12 @@
 
 #include "imgui_fullscreen.h"
 #include "gpu_device.h"
+#include "image.h"
 #include "imgui_animated.h"
 
 #include "common/assert.h"
 #include "common/easing.h"
 #include "common/file_system.h"
-#include "common/image.h"
 #include "common/log.h"
 #include "common/lru_cache.h"
 #include "common/path.h"
@@ -42,8 +42,8 @@ using MessageDialogCallbackVariant = std::variant<InfoMessageDialogCallback, Con
 
 static constexpr float MENU_BACKGROUND_ANIMATION_TIME = 0.5f;
 
-static std::optional<Common::RGBA8Image> LoadTextureImage(const char* path);
-static std::shared_ptr<GPUTexture> UploadTexture(const char* path, const Common::RGBA8Image& image);
+static std::optional<RGBA8Image> LoadTextureImage(const char* path);
+static std::shared_ptr<GPUTexture> UploadTexture(const char* path, const RGBA8Image& image);
 static void TextureLoaderThread();
 
 static void DrawFileSelector();
@@ -96,7 +96,7 @@ static std::atomic_bool s_texture_load_thread_quit{false};
 static std::mutex s_texture_load_mutex;
 static std::condition_variable s_texture_load_cv;
 static std::deque<std::string> s_texture_load_queue;
-static std::deque<std::pair<std::string, Common::RGBA8Image>> s_texture_upload_queue;
+static std::deque<std::pair<std::string, RGBA8Image>> s_texture_upload_queue;
 static std::thread s_texture_load_thread;
 
 static bool s_choice_dialog_open = false;
@@ -268,9 +268,9 @@ const std::shared_ptr<GPUTexture>& ImGuiFullscreen::GetPlaceholderTexture()
   return s_placeholder_texture;
 }
 
-std::optional<Common::RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char* path)
+std::optional<RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char* path)
 {
-  std::optional<Common::RGBA8Image> image;
+  std::optional<RGBA8Image> image;
 
   std::optional<std::vector<u8>> data;
   if (Path::IsAbsolute(path))
@@ -279,7 +279,7 @@ std::optional<Common::RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char*
     data = Host::ReadResourceFile(path, true);
   if (data.has_value())
   {
-    image = Common::RGBA8Image();
+    image = RGBA8Image();
     if (!image->LoadFromBuffer(path, data->data(), data->size()))
     {
       Log_ErrorPrintf("Failed to read texture resource '%s'", path);
@@ -294,7 +294,7 @@ std::optional<Common::RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char*
   return image;
 }
 
-std::shared_ptr<GPUTexture> ImGuiFullscreen::UploadTexture(const char* path, const Common::RGBA8Image& image)
+std::shared_ptr<GPUTexture> ImGuiFullscreen::UploadTexture(const char* path, const RGBA8Image& image)
 {
   std::unique_ptr<GPUTexture> texture =
     g_gpu_device->FetchTexture(image.GetWidth(), image.GetHeight(), 1, 1, 1, GPUTexture::Type::Texture,
@@ -312,7 +312,7 @@ std::shared_ptr<GPUTexture> ImGuiFullscreen::UploadTexture(const char* path, con
 std::shared_ptr<GPUTexture> ImGuiFullscreen::LoadTexture(const std::string_view& path)
 {
   std::string path_str(path);
-  std::optional<Common::RGBA8Image> image(LoadTextureImage(path_str.c_str()));
+  std::optional<RGBA8Image> image(LoadTextureImage(path_str.c_str()));
   if (image.has_value())
   {
     std::shared_ptr<GPUTexture> ret(UploadTexture(path_str.c_str(), image.value()));
@@ -362,7 +362,7 @@ void ImGuiFullscreen::UploadAsyncTextures()
   std::unique_lock lock(s_texture_load_mutex);
   while (!s_texture_upload_queue.empty())
   {
-    std::pair<std::string, Common::RGBA8Image> it(std::move(s_texture_upload_queue.front()));
+    std::pair<std::string, RGBA8Image> it(std::move(s_texture_upload_queue.front()));
     s_texture_upload_queue.pop_front();
     lock.unlock();
 
@@ -395,7 +395,7 @@ void ImGuiFullscreen::TextureLoaderThread()
       s_texture_load_queue.pop_front();
 
       lock.unlock();
-      std::optional<Common::RGBA8Image> image(LoadTextureImage(path.c_str()));
+      std::optional<RGBA8Image> image(LoadTextureImage(path.c_str()));
       lock.lock();
 
       // don't bother queuing back if it doesn't exist
diff --git a/src/util/imgui_manager.cpp b/src/util/imgui_manager.cpp
index bf5d4c993..c9006b796 100644
--- a/src/util/imgui_manager.cpp
+++ b/src/util/imgui_manager.cpp
@@ -4,6 +4,7 @@
 #include "imgui_manager.h"
 #include "gpu_device.h"
 #include "host.h"
+#include "image.h"
 #include "imgui_fullscreen.h"
 #include "input_manager.h"
 
@@ -11,7 +12,6 @@
 #include "common/easing.h"
 #include "common/error.h"
 #include "common/file_system.h"
-#include "common/image.h"
 #include "common/log.h"
 #include "common/string_util.h"
 #include "common/timer.h"
@@ -1040,7 +1040,7 @@ void ImGuiManager::UpdateSoftwareCursorTexture(u32 index)
     return;
   }
 
-  Common::RGBA8Image image;
+  RGBA8Image image;
   if (!image.LoadFromFile(sc.image_path.c_str()))
   {
     Log_ErrorPrintf("Failed to load software cursor %u image '%s'", index, sc.image_path.c_str());
diff --git a/src/util/postprocessing_shader_fx.cpp b/src/util/postprocessing_shader_fx.cpp
index b39b2424d..9d5b6f81b 100644
--- a/src/util/postprocessing_shader_fx.cpp
+++ b/src/util/postprocessing_shader_fx.cpp
@@ -1,7 +1,8 @@
-// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
 
 #include "postprocessing_shader_fx.h"
+#include "image.h"
 #include "input_manager.h"
 #include "shadergen.h"
 
@@ -12,7 +13,6 @@
 #include "common/assert.h"
 #include "common/error.h"
 #include "common/file_system.h"
-#include "common/image.h"
 #include "common/log.h"
 #include "common/path.h"
 #include "common/progress_callback.h"
@@ -928,7 +928,7 @@ bool PostProcessing::ReShadeFXShader::CreatePasses(GPUTexture::Format backbuffer
         return false;
       }
 
-      Common::RGBA8Image image;
+      RGBA8Image image;
       if (const std::string image_path =
             Path::Combine(EmuFolders::Shaders, Path::Combine("reshade" FS_OSPATH_SEPARATOR_STR "Textures", source));
           !image.LoadFromFile(image_path.c_str()))
diff --git a/src/util/util.props b/src/util/util.props
index 8b477f8b1..c0f46ca5f 100644
--- a/src/util/util.props
+++ b/src/util/util.props
@@ -8,7 +8,7 @@
       <PreprocessorDefinitions>ENABLE_CUBEB=1;ENABLE_SDL2=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(Platform)'!='ARM64'">%(PreprocessorDefinitions);ENABLE_OPENGL=1;ENABLE_VULKAN=1</PreprocessorDefinitions>
       <PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">%(PreprocessorDefinitions);SOUNDTOUCH_USE_NEON</PreprocessorDefinitions>
-      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\soundtouch\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\libchdr\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\d3d12ma\include;$(SolutionDir)dep\zstd\lib</AdditionalIncludeDirectories>
+      <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\soundtouch\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\simpleini\include;$(SolutionDir)dep\libchdr\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\d3d12ma\include;$(SolutionDir)dep\zstd\lib;$(SolutionDir)dep\stb\include</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories Condition="'$(Platform)'!='ARM64'">%(AdditionalIncludeDirectories);$(SolutionDir)dep\glad\include;$(SolutionDir)dep\vulkan\include;$(SolutionDir)dep\glslang</AdditionalIncludeDirectories>
     </ClCompile>
   </ItemDefinitionGroup>
diff --git a/src/util/util.vcxproj b/src/util/util.vcxproj
index b06947c44..a64802a7b 100644
--- a/src/util/util.vcxproj
+++ b/src/util/util.vcxproj
@@ -3,6 +3,7 @@
   <Import Project="..\..\dep\msvc\vsprops\Configurations.props" />
   <ItemGroup>
     <ClInclude Include="gpu_types.h" />
+    <ClInclude Include="image.h" />
     <ClInclude Include="imgui_animated.h" />
     <ClInclude Include="audio_stream.h" />
     <ClInclude Include="cd_image.h" />
@@ -158,6 +159,7 @@
       <ExcludedFromBuild>true</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="http_downloader_winhttp.cpp" />
+    <ClCompile Include="image.cpp" />
     <ClCompile Include="imgui_fullscreen.cpp" />
     <ClCompile Include="imgui_manager.cpp" />
     <ClCompile Include="ini_settings_interface.cpp" />
@@ -263,6 +265,9 @@
     <ProjectReference Include="..\..\dep\glslang\glslang.vcxproj" Condition="'$(Platform)'!='ARM64'">
       <Project>{7f909e29-4808-4bd9-a60c-56c51a3aaec2}</Project>
     </ProjectReference>
+    <ProjectReference Include="..\..\dep\stb\stb.vcxproj">
+      <Project>{ed601289-ac1a-46b8-a8ed-17db9eb73423}</Project>
+    </ProjectReference>
     <ProjectReference Include="..\..\dep\zstd\zstd.vcxproj">
       <Project>{73ee0c55-6ffe-44e7-9c12-baa52434a797}</Project>
     </ProjectReference>
diff --git a/src/util/util.vcxproj.filters b/src/util/util.vcxproj.filters
index 1c7cea475..8850b1df7 100644
--- a/src/util/util.vcxproj.filters
+++ b/src/util/util.vcxproj.filters
@@ -73,6 +73,7 @@
     <ClInclude Include="opengl_context_egl_x11.h" />
     <ClInclude Include="opengl_context_wgl.h" />
     <ClInclude Include="gpu_types.h" />
+    <ClInclude Include="image.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="jit_code_buffer.cpp" />
@@ -154,11 +155,7 @@
     <ClCompile Include="opengl_context_egl_wayland.cpp" />
     <ClCompile Include="opengl_context_egl_x11.cpp" />
     <ClCompile Include="opengl_context_wgl.cpp" />
-  </ItemGroup>
-  <ItemGroup>
-    <Filter Include="gl">
-      <UniqueIdentifier>{e637fc5b-2483-4a31-abc3-89a16d45c223}</UniqueIdentifier>
-    </Filter>
+    <ClCompile Include="image.cpp" />
   </ItemGroup>
   <ItemGroup>
     <None Include="metal_shaders.metal" />