#if defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND)
#include <SDL3_image/SDL_image.h>
#include "IMG_utils.h"
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>
#include <Foundation/Foundation.h>
#if (TARGET_OS_IPHONE == 1) || (TARGET_IPHONE_SIMULATOR == 1)
#import <MobileCoreServices/MobileCoreServices.h>
#import <ImageIO/ImageIO.h>
#import <UIKit/UIImage.h>
#else
#include <ApplicationServices/ApplicationServices.h>
#endif
static size_t MyProviderGetBytesCallback(void* userdata, void* quartz_buffer, size_t the_count)
{
Sint64 size = SDL_ReadIO((SDL_IOStream *)userdata, quartz_buffer, the_count);
if (size <= 0) {
return 0;
}
return (size_t)size;
}
static void MyProviderReleaseInfoCallback(void* userdata)
{
}
static void MyProviderRewindCallback(void* userdata)
{
SDL_SeekIO((SDL_IOStream *)userdata, 0, SDL_IO_SEEK_SET);
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
off_t MyProviderSkipForwardBytesCallback(void* userdata, off_t the_count)
{
off_t start_position = SDL_TellIO((SDL_IOStream *)userdata);
SDL_SeekIO((SDL_IOStream *)userdata, the_count, SDL_IO_SEEK_CUR);
off_t end_position = SDL_TellIO((SDL_IOStream *)userdata);
return (end_position - start_position);
}
#else
static void MyProviderSkipBytesCallback(void* userdata, size_t the_count)
{
SDL_SeekIO((SDL_IOStream *)userdata, the_count, SDL_IO_SEEK_CUR);
}
#endif
static CGImageSourceRef CreateCGImageSourceFromIOStream(SDL_IOStream * rw_ops, CFDictionaryRef hints_and_options)
{
CGImageSourceRef source_ref;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
CGDataProviderSequentialCallbacks provider_callbacks =
{
0,
MyProviderGetBytesCallback,
MyProviderSkipForwardBytesCallback,
MyProviderRewindCallback,
MyProviderReleaseInfoCallback
};
CGDataProviderRef data_provider = CGDataProviderCreateSequential(rw_ops, &provider_callbacks);
#else
CGDataProviderCallbacks provider_callbacks =
{
MyProviderGetBytesCallback,
MyProviderSkipBytesCallback,
MyProviderRewindCallback,
MyProviderReleaseInfoCallback
};
CGDataProviderRef data_provider = CGDataProviderCreate(rw_ops, &provider_callbacks);
#endif
source_ref = CGImageSourceCreateWithDataProvider(data_provider, hints_and_options);
CGDataProviderRelease(data_provider);
return source_ref;
}
static CGImageSourceRef CreateCGImageSourceFromFile(const char* the_path)
{
CFURLRef the_url = NULL;
CGImageSourceRef source_ref = NULL;
CFStringRef cf_string = NULL;
cf_string = CFStringCreateWithCString(NULL, the_path, kCFStringEncodingUTF8);
if (!cf_string) {
return NULL;
}
the_url = CFURLCreateWithFileSystemPath(NULL, cf_string, kCFURLPOSIXPathStyle, false);
CFRelease(cf_string);
if(!the_url)
{
return NULL;
}
source_ref = CGImageSourceCreateWithURL(the_url, NULL);
CFRelease(the_url);
return source_ref;
}
static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
{
CGImageRef image_ref = NULL;
if (!image_source) {
return NULL;
}
image_ref = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
if (!image_ref) {
SDL_SetError("CGImageSourceCreateImageAtIndex() failed");
}
return image_ref;
}
static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
{
CFDictionaryRef hint_dictionary = NULL;
if(uti_string_hint != NULL)
{
CFStringRef the_keys[1];
CFStringRef the_values[1];
the_keys[0] = kCGImageSourceTypeIdentifierHint;
the_values[0] = uti_string_hint;
hint_dictionary = CFDictionaryCreate(NULL, (const void**)&the_keys, (const void**)&the_values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
return hint_dictionary;
}
static SDL_Surface* Create_SDL_Surface_From_CGImage_RGB(CGImageRef image_ref)
{
size_t w = CGImageGetWidth(image_ref);
size_t h = CGImageGetHeight(image_ref);
CGRect rect = {{0, 0}, {w, h}};
CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image_ref);
size_t bits_per_component = 8;
SDL_Surface* surface;
Uint32 format;
CGContextRef bitmap_context;
CGBitmapInfo bitmap_info;
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
if (alpha == kCGImageAlphaNone ||
alpha == kCGImageAlphaNoneSkipFirst ||
alpha == kCGImageAlphaNoneSkipLast) {
bitmap_info = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
format = SDL_PIXELFORMAT_XRGB8888;
} else {
bitmap_info = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
format = SDL_PIXELFORMAT_ARGB8888;
}
surface = SDL_CreateSurface((int)w, (int)h, format);
if (surface)
{
bitmap_context = CGBitmapContextCreate(
surface->pixels,
surface->w,
surface->h,
bits_per_component,
surface->pitch,
color_space,
bitmap_info
);
CGContextDrawImage(bitmap_context, rect, image_ref);
CGContextRelease(bitmap_context);
if ((bitmap_info & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst) {
int i, j;
Uint8 *p = (Uint8 *)surface->pixels;
for (i = surface->h * surface->pitch/4; i--; ) {
#if __LITTLE_ENDIAN__
Uint8 A = p[3];
if (A) {
for (j = 0; j < 3; ++j) {
p[j] = (p[j] * 255) / A;
}
}
#else
Uint8 A = p[0];
if (A) {
for (j = 1; j < 4; ++j) {
p[j] = (p[j] * 255) / A;
}
}
#endif
p += 4;
}
}
}
if (color_space)
{
CGColorSpaceRelease(color_space);
}
return surface;
}
static SDL_Surface* Create_SDL_Surface_From_CGImage_Index(CGImageRef image_ref)
{
size_t w = CGImageGetWidth(image_ref);
size_t h = CGImageGetHeight(image_ref);
size_t bits_per_pixel = CGImageGetBitsPerPixel(image_ref);
size_t bytes_per_row = CGImageGetBytesPerRow(image_ref);
SDL_Surface* surface;
CGColorSpaceRef color_space = CGImageGetColorSpace(image_ref);
CGColorSpaceRef base_color_space = CGColorSpaceGetBaseColorSpace(color_space);
size_t num_components = CGColorSpaceGetNumberOfComponents(base_color_space);
size_t num_entries = CGColorSpaceGetColorTableCount(color_space);
uint8_t *entry, *entries;
entries = SDL_stack_alloc(uint8_t, num_components * num_entries);
if (entries == NULL) {
SDL_OutOfMemory();
return NULL;
}
if (num_components != 3) {
SDL_SetError("Unknown colorspace components %lu", num_components);
return NULL;
}
if (bits_per_pixel != 8) {
SDL_SetError("Unknown bits_per_pixel %lu", bits_per_pixel);
return NULL;
}
CGColorSpaceGetColorTable(color_space, entries);
surface = SDL_CreateSurface((int)w, (int)h, SDL_PIXELFORMAT_INDEX8);
if (surface) {
uint8_t* pixels = (uint8_t*)surface->pixels;
CGDataProviderRef provider = CGImageGetDataProvider(image_ref);
NSData* data = (id)CGDataProviderCopyData(provider);
[data autorelease];
const uint8_t* bytes = [data bytes];
size_t i;
if (num_entries > 0) {
SDL_Palette* palette = SDL_CreateSurfacePalette(surface);
if (palette) {
if (num_entries > (size_t)palette->ncolors) {
num_entries = (size_t)palette->ncolors;
}
palette->ncolors = (int)num_entries;
for (i = 0, entry = entries; i < num_entries; ++i) {
palette->colors[i].r = entry[0];
palette->colors[i].g = entry[1];
palette->colors[i].b = entry[2];
palette->colors[i].a = SDL_ALPHA_OPAQUE;
entry += num_components;
}
}
}
for (i = 0; i < h; ++i) {
SDL_memcpy(pixels, bytes, w);
pixels += surface->pitch;
bytes += bytes_per_row;
}
}
SDL_stack_free(entries);
return surface;
}
static SDL_Surface *Create_SDL_Surface_From_CGImage(CGImageRef image_ref, CFDictionaryRef properties)
{
SDL_Surface *surface;
CGColorSpaceRef color_space = CGImageGetColorSpace(image_ref);
if (CGColorSpaceGetModel(color_space) == kCGColorSpaceModelIndexed) {
surface = Create_SDL_Surface_From_CGImage_Index(image_ref);
} else {
surface = Create_SDL_Surface_From_CGImage_RGB(image_ref);
}
if (surface && properties) {
CFNumberRef numval;
if (CFDictionaryGetValueIfPresent(properties, kCGImagePropertyOrientation, (const void **)&numval)) {
CGImagePropertyOrientation orientation;
CFNumberGetValue(numval, kCFNumberSInt32Type, &orientation);
surface = IMG_ApplyOrientation(surface, orientation);
}
}
return surface;
}
static bool Internal_isType (SDL_IOStream *rw_ops, CFStringRef uti_string_to_test)
{
bool is_type = false;
if (rw_ops == NULL) {
return false;
}
Sint64 start = SDL_TellIO(rw_ops);
CGImageSourceRef image_source = CreateCGImageSourceFromIOStream(rw_ops, NULL);
if (NULL == image_source) {
SDL_SeekIO(rw_ops, start, SEEK_SET);
return 0;
}
CFStringRef uti_type = CGImageSourceGetType(image_source);
is_type = UTTypeConformsTo(uti_string_to_test, uti_type);
CFRelease(image_source);
SDL_SeekIO(rw_ops, start, SEEK_SET);
return is_type;
}
#ifdef BMP_USES_IMAGEIO
bool IMG_isCUR(SDL_IOStream *src)
{
return Internal_isType(src, CFSTR("com.microsoft.cur"));
}
bool IMG_isICO(SDL_IOStream *src)
{
return Internal_isType(src, kUTTypeICO);
}
bool IMG_isBMP(SDL_IOStream *src)
{
return Internal_isType(src, kUTTypeBMP);
}
#endif
bool IMG_isGIF(SDL_IOStream *src)
{
return Internal_isType(src, kUTTypeGIF);
}
#ifdef JPG_USES_IMAGEIO
bool IMG_isJPG(SDL_IOStream *src)
{
return Internal_isType(src, kUTTypeJPEG);
}
#endif
bool IMG_isTGA(SDL_IOStream *src)
{
return Internal_isType(src, CFSTR("com.truevision.tga-image"));
}
bool IMG_isTIF(SDL_IOStream *src)
{
return Internal_isType(src, kUTTypeTIFF);
}
static SDL_Surface *LoadImageFromIOStream(SDL_IOStream *rw_ops, CFStringRef uti_string_hint)
{
SDL_Surface *surface = NULL;
CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_hint);
CGImageSourceRef image_source = CreateCGImageSourceFromIOStream(rw_ops, hint_dictionary);
if (hint_dictionary) {
CFRelease(hint_dictionary);
}
if (image_source) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(image_source, 0, NULL);
CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
if (image_ref) {
surface = Create_SDL_Surface_From_CGImage(image_ref, properties);
CFRelease(image_ref);
}
if (properties) {
CFRelease(properties);
}
CFRelease(image_source);
}
return surface;
}
static SDL_Surface *LoadImageFromFile(const char *file)
{
SDL_Surface *surface = NULL;
CGImageSourceRef image_source = CreateCGImageSourceFromFile(file);
if (image_source) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(image_source, 0, NULL);
CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
if (image_ref) {
surface = Create_SDL_Surface_From_CGImage(image_ref, properties);
CFRelease(image_ref);
}
if (properties) {
CFRelease(properties);
}
CFRelease(image_source);
}
return surface;
}
#ifdef BMP_USES_IMAGEIO
SDL_Surface* IMG_LoadCUR_IO (SDL_IOStream *src)
{
return LoadImageFromIOStream(src, CFSTR("com.microsoft.cur"));
}
SDL_Surface* IMG_LoadICO_IO (SDL_IOStream *src)
{
return LoadImageFromIOStream(src, kUTTypeICO);
}
SDL_Surface* IMG_LoadBMP_IO (SDL_IOStream *src)
{
return LoadImageFromIOStream(src, kUTTypeBMP);
}
#endif
SDL_Surface* IMG_LoadGIF_IO (SDL_IOStream *src)
{
return LoadImageFromIOStream (src, kUTTypeGIF);
}
#ifdef JPG_USES_IMAGEIO
SDL_Surface* IMG_LoadJPG_IO (SDL_IOStream *src)
{
return LoadImageFromIOStream (src, kUTTypeJPEG);
}
#endif
#ifdef PNG_USES_IMAGEIO
SDL_Surface* IMG_LoadPNG_ImageIO (SDL_IOStream *src)
{
return LoadImageFromIOStream (src, kUTTypePNG);
}
#endif
SDL_Surface* IMG_LoadTGA_IO (SDL_IOStream *src)
{
return LoadImageFromIOStream(src, CFSTR("com.truevision.tga-image"));
}
SDL_Surface* IMG_LoadTIF_IO (SDL_IOStream *src)
{
return LoadImageFromIOStream(src, kUTTypeTIFF);
}
SDL_Surface* IMG_Load (const char *file)
{
SDL_Surface *surface = NULL;
char *ext = SDL_strrchr(file, '.');
if (ext) {
ext++;
}
if (ext && (SDL_strcasecmp(ext, "ico") == 0 || SDL_strcasecmp(ext, "cur") == 0)) {
} else {
surface = LoadImageFromFile(file);
}
if (!surface) {
SDL_IOStream *src = SDL_IOFromFile(file, "rb");
if (!src) {
return NULL;
}
surface = IMG_LoadTyped_IO(src, 1, ext);
}
return surface;
}
#endif