SDL_image: Added APNG support to the Xcode build

From abd8ee5a5234055ddcdfa6c6651f9c037a4d1ee6 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 5 Jan 2026 12:30:07 -0800
Subject: [PATCH] Added APNG support to the Xcode build

---
 Xcode/SDL_image.xcodeproj/project.pbxproj     | 28 +++++++++++
 Xcode/config.xcconfig                         | 10 ++--
 .../showanim.xcodeproj/project.pbxproj        | 46 +++++++++++++++++++
 src/IMG_libpng.c                              |  8 ++++
 4 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/Xcode/SDL_image.xcodeproj/project.pbxproj b/Xcode/SDL_image.xcodeproj/project.pbxproj
index d50b51f00..97dccad86 100644
--- a/Xcode/SDL_image.xcodeproj/project.pbxproj
+++ b/Xcode/SDL_image.xcodeproj/project.pbxproj
@@ -96,6 +96,13 @@
 			remoteGlobalIDString = F307EEDC28288CCF003915D7;
 			remoteInfo = jxl;
 		};
+		F31BA83E2F0C56BA00646176 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F31BA7F62F0C417B00646176 /* png.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = F3D87D21281EA9C3005DA540;
+			remoteInfo = png;
+		};
 		F35475DB2829BA80007E9EDA /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = F35475D42829BA80007E9EDA /* avif.xcodeproj */;
@@ -193,6 +200,7 @@
 		BE1FA72E07AF4C45004B6283 /* SDL3_image.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDL3_image.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		F307EFA82828C8FA003915D7 /* jxl.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = jxl.xcodeproj; path = jxl/jxl.xcodeproj; sourceTree = "<group>"; };
 		F31094C2282AE42D008EF641 /* IMG_stb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = IMG_stb.c; path = ../src/IMG_stb.c; sourceTree = "<group>"; };
+		F31BA7F62F0C417B00646176 /* png.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = png.xcodeproj; path = png/png.xcodeproj; sourceTree = "<group>"; };
 		F34123BF2D41A75D00D6C2B7 /* INSTALL.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = INSTALL.md; sourceTree = "<group>"; };
 		F34123C32D41A79D00D6C2B7 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = LICENSE.txt; path = ../../../LICENSE.txt; sourceTree = "<group>"; };
 		F34123C52D41A7D800D6C2B7 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../../README.md; sourceTree = "<group>"; };
@@ -268,6 +276,7 @@
 				F3547625282AE1C6007E9EDA /* config.xcconfig */,
 				F35475D42829BA80007E9EDA /* avif.xcodeproj */,
 				F307EFA82828C8FA003915D7 /* jxl.xcodeproj */,
+				F31BA7F62F0C417B00646176 /* png.xcodeproj */,
 				F3D87D15281EA88F005DA540 /* webp.xcodeproj */,
 				F59C70FC00D5CB5801000001 /* pkg-support */,
 				0153844A006D81B07F000001 /* Public Headers */,
@@ -346,6 +355,14 @@
 			name = Products;
 			sourceTree = "<group>";
 		};
+		F31BA8322F0C56BA00646176 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				F31BA83F2F0C56BA00646176 /* png.framework */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 		F34123BE2D41A71500D6C2B7 /* framework */ = {
 			isa = PBXGroup;
 			children = (
@@ -480,6 +497,10 @@
 					ProductGroup = F307EFA92828C8FA003915D7 /* Products */;
 					ProjectRef = F307EFA82828C8FA003915D7 /* jxl.xcodeproj */;
 				},
+				{
+					ProductGroup = F31BA8322F0C56BA00646176 /* Products */;
+					ProjectRef = F31BA7F62F0C417B00646176 /* png.xcodeproj */;
+				},
 				{
 					ProductGroup = F3D87D16281EA88F005DA540 /* Products */;
 					ProjectRef = F3D87D15281EA88F005DA540 /* webp.xcodeproj */;
@@ -502,6 +523,13 @@
 			remoteRef = F307EFAC2828C8FA003915D7 /* PBXContainerItemProxy */;
 			sourceTree = BUILT_PRODUCTS_DIR;
 		};
+		F31BA83F2F0C56BA00646176 /* png.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = png.framework;
+			remoteRef = F31BA83E2F0C56BA00646176 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 		F35475DC2829BA80007E9EDA /* avif.framework */ = {
 			isa = PBXReferenceProxy;
 			fileType = wrapper.framework;
diff --git a/Xcode/config.xcconfig b/Xcode/config.xcconfig
index 67e24606b..73d8718cc 100644
--- a/Xcode/config.xcconfig
+++ b/Xcode/config.xcconfig
@@ -18,11 +18,15 @@
 //JXL_PREPROCESSOR_DEFINITIONS = LOAD_JXL
 //JXL_FRAMEWORK_LDFLAGS = -weak_framework jxl
 
+// Uncomment these lines to enable libpng support
+// If you do this, you should run external/download.sh to download the decode libraries and add png.framework to your application bundle.
+//PNG_PREPROCESSOR_DEFINITIONS = SDL_IMAGE_LIBPNG INCLUDE_PNG_FRAMEWORK
+//PNG_FRAMEWORK_LDFLAGS = -weak_framework png
+
 // Uncomment these lines to enable WebP support
 // If you do this, you should run external/download.sh to download the decode libraries and add webp.framework to your application bundle.
 //WEBP_PREPROCESSOR_DEFINITIONS = LOAD_WEBP
 //WEBP_FRAMEWORK_LDFLAGS = -weak_framework webp
 
-CONFIG_PREPROCESSOR_DEFINITIONS = $(inherited) $(AVIF_PREPROCESSOR_DEFINITIONS) $(JXL_PREPROCESSOR_DEFINITIONS) $(WEBP_PREPROCESSOR_DEFINITIONS)
-CONFIG_FRAMEWORK_LDFLAGS = $(inherited) $(AVIF_FRAMEWORK_LDFLAGS) $(JXL_FRAMEWORK_LDFLAGS) $(WEBP_FRAMEWORK_LDFLAGS)
-
+CONFIG_PREPROCESSOR_DEFINITIONS = $(inherited) $(AVIF_PREPROCESSOR_DEFINITIONS) $(JXL_PREPROCESSOR_DEFINITIONS) $(PNG_PREPROCESSOR_DEFINITIONS) $(WEBP_PREPROCESSOR_DEFINITIONS)
+CONFIG_FRAMEWORK_LDFLAGS = $(inherited) $(AVIF_FRAMEWORK_LDFLAGS) $(JXL_FRAMEWORK_LDFLAGS) $(PNG_FRAMEWORK_LDFLAGS) $(WEBP_FRAMEWORK_LDFLAGS)
diff --git a/Xcode/showanim/showanim.xcodeproj/project.pbxproj b/Xcode/showanim/showanim.xcodeproj/project.pbxproj
index d01dfa9c1..488d7ab1c 100644
--- a/Xcode/showanim/showanim.xcodeproj/project.pbxproj
+++ b/Xcode/showanim/showanim.xcodeproj/project.pbxproj
@@ -7,6 +7,12 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		F31BA8222F0C524300646176 /* png.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F31BA8212F0C524300646176 /* png.framework */; };
+		F31BA8232F0C524300646176 /* png.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = F31BA8212F0C524300646176 /* png.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		F31BA8252F0C524E00646176 /* png.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F31BA8242F0C524E00646176 /* png.framework */; };
+		F31BA8262F0C524E00646176 /* png.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = F31BA8242F0C524E00646176 /* png.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		F31BA8282F0C525C00646176 /* png.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F31BA8272F0C525C00646176 /* png.framework */; };
+		F31BA8292F0C525C00646176 /* png.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = F31BA8272F0C525C00646176 /* png.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		F341283C2D4B496D00D6C2B7 /* SDL3.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = F34123152D4077A600D6C2B7 /* SDL3.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		F341283D2D4B498100D6C2B7 /* SDL3.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = F34123152D4077A600D6C2B7 /* SDL3.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		F341283E2D4B498700D6C2B7 /* SDL3.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = F34123152D4077A600D6C2B7 /* SDL3.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -34,6 +40,13 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
+		F31BA84B2F0C56BA00646176 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = F31BA81E2F0C521E00646176 /* png.xcodeproj */;
+			proxyType = 2;
+			remoteGlobalIDString = F3D87D21281EA9C3005DA540;
+			remoteInfo = png;
+		};
 		F34123142D4077A600D6C2B7 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = F344001D2D401E16003F26D7 /* SDL.xcodeproj */;
@@ -110,6 +123,7 @@
 				F39CD44B281DC6C8006CF638 /* SDL3_image.framework in Copy Frameworks */,
 				F35475F92829BAC7007E9EDA /* avif.framework in Copy Frameworks */,
 				F35474562828CDE0007E9EDA /* jxl.framework in Copy Frameworks */,
+				F31BA8292F0C525C00646176 /* png.framework in Copy Frameworks */,
 				F3E949DB281EAC3500B8F4EA /* webp.framework in Copy Frameworks */,
 			);
 			name = "Copy Frameworks";
@@ -125,6 +139,7 @@
 				F39CD452281DC9CE006CF638 /* SDL3_image.framework in Copy Frameworks */,
 				F35475FA2829BACC007E9EDA /* avif.framework in Copy Frameworks */,
 				F35474552828CDDB007E9EDA /* jxl.framework in Copy Frameworks */,
+				F31BA8262F0C524E00646176 /* png.framework in Copy Frameworks */,
 				F3E949DC281EAC9600B8F4EA /* webp.framework in Copy Frameworks */,
 			);
 			name = "Copy Frameworks";
@@ -140,6 +155,7 @@
 				F3ED80FF281DA63000C33C5B /* SDL3_image.framework in Copy Frameworks */,
 				F35475FB2829BAD0007E9EDA /* avif.framework in Copy Frameworks */,
 				F35474502828CAF5007E9EDA /* jxl.framework in Copy Frameworks */,
+				F31BA8232F0C524300646176 /* png.framework in Copy Frameworks */,
 				F3E949DD281EAC9C00B8F4EA /* webp.framework in Copy Frameworks */,
 			);
 			name = "Copy Frameworks";
@@ -148,6 +164,10 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
+		F31BA81E2F0C521E00646176 /* png.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = png.xcodeproj; path = ../png/png.xcodeproj; sourceTree = SOURCE_ROOT; };
+		F31BA8212F0C524300646176 /* png.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = png.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		F31BA8242F0C524E00646176 /* png.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = png.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		F31BA8272F0C525C00646176 /* png.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = png.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		F344001D2D401E16003F26D7 /* SDL.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SDL.xcodeproj; path = ../../../SDL/Xcode/SDL/SDL.xcodeproj; sourceTree = SOURCE_ROOT; };
 		F35474462828CADB007E9EDA /* jxl.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = jxl.xcodeproj; path = ../jxl/jxl.xcodeproj; sourceTree = "<group>"; };
 		F35475E52829BAB1007E9EDA /* avif.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = avif.xcodeproj; path = ../avif/avif.xcodeproj; sourceTree = "<group>"; };
@@ -170,6 +190,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				F34128402D4B4A5600D6C2B7 /* SDL3.framework in Frameworks */,
+				F31BA8222F0C524300646176 /* png.framework in Frameworks */,
 				F3ED80F5281DA3F600C33C5B /* SDL3_image.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -179,6 +200,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				F341283F2D4B4A4F00D6C2B7 /* SDL3.framework in Frameworks */,
+				F31BA8252F0C524E00646176 /* png.framework in Frameworks */,
 				F3ED80E3281DA16500C33C5B /* SDL3_image.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -188,6 +210,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				F34128412D4B4A5C00D6C2B7 /* SDL3.framework in Frameworks */,
+				F31BA8282F0C525C00646176 /* png.framework in Frameworks */,
 				F3ED8113281DC13D00C33C5B /* SDL3_image.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -195,6 +218,14 @@
 /* End PBXFrameworksBuildPhase section */
 
 /* Begin PBXGroup section */
+		F31BA8342F0C56BA00646176 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				F31BA84C2F0C56BA00646176 /* png.framework */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
 		F341230F2D4077A600D6C2B7 /* Products */ = {
 			isa = PBXGroup;
 			children = (
@@ -239,6 +270,7 @@
 				F3ED80CD281D9ED600C33C5B /* SDL_image.xcodeproj */,
 				F35475E52829BAB1007E9EDA /* avif.xcodeproj */,
 				F35474462828CADB007E9EDA /* jxl.xcodeproj */,
+				F31BA81E2F0C521E00646176 /* png.xcodeproj */,
 				F3E949D5281EAC1B00B8F4EA /* webp.xcodeproj */,
 				F3ED80AB281D9E8800C33C5B /* Shared */,
 				F3ED80BA281D9E8900C33C5B /* macOS */,
@@ -284,6 +316,9 @@
 		F3ED80E2281DA16500C33C5B /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				F31BA8272F0C525C00646176 /* png.framework */,
+				F31BA8242F0C524E00646176 /* png.framework */,
+				F31BA8212F0C524300646176 /* png.framework */,
 				F3ED8104281DADB900C33C5B /* macOS */,
 				F3ED8105281DADC900C33C5B /* iOS */,
 				F3ED810D281DC07200C33C5B /* tvOS */,
@@ -410,6 +445,10 @@
 					ProductGroup = F35474472828CADB007E9EDA /* Products */;
 					ProjectRef = F35474462828CADB007E9EDA /* jxl.xcodeproj */;
 				},
+				{
+					ProductGroup = F31BA8342F0C56BA00646176 /* Products */;
+					ProjectRef = F31BA81E2F0C521E00646176 /* png.xcodeproj */;
+				},
 				{
 					ProductGroup = F341230F2D4077A600D6C2B7 /* Products */;
 					ProjectRef = F344001D2D401E16003F26D7 /* SDL.xcodeproj */;
@@ -433,6 +472,13 @@
 /* End PBXProject section */
 
 /* Begin PBXReferenceProxy section */
+		F31BA84C2F0C56BA00646176 /* png.framework */ = {
+			isa = PBXReferenceProxy;
+			fileType = wrapper.framework;
+			path = png.framework;
+			remoteRef = F31BA84B2F0C56BA00646176 /* PBXContainerItemProxy */;
+			sourceTree = BUILT_PRODUCTS_DIR;
+		};
 		F34123152D4077A600D6C2B7 /* SDL3.framework */ = {
 			isa = PBXReferenceProxy;
 			fileType = wrapper.framework;
diff --git a/src/IMG_libpng.c b/src/IMG_libpng.c
index 1f9fc385a..07f3f87ab 100644
--- a/src/IMG_libpng.c
+++ b/src/IMG_libpng.c
@@ -34,7 +34,11 @@
 #include "IMG_anim_decoder.h"
 
 #ifdef SDL_IMAGE_LIBPNG
+#ifdef INCLUDE_PNG_FRAMEWORK
+#include <png/png.h>
+#else
 #include <png.h>
+#endif
 
 #if defined(LOAD_LIBPNG_DYNAMIC) && defined(SDL_ELF_NOTE_DLOPEN)
 SDL_ELF_NOTE_DLOPEN(
@@ -198,6 +202,10 @@ static struct
         }
 #endif
 
+#ifdef __APPLE__
+/* Need to turn off optimizations so weak framework load check works */
+__attribute__((optnone))
+#endif
 static bool IMG_InitPNG(void)
 {
     if (lib.loaded == 0) {