Something went wrong. Try again.
if you push this repo to github you get banned like its an instant ban you don't want to get banned so do
Something went wrong. Try again.
81 kB · 1,740 lines
at dev
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741@import Darwin;@import CoreGraphics;#include "bridge.h"#include "LC32CoreGraphicsHost.h"#include "../../GuestFrameworks/CoreGraphics/LC32CoreGraphicsBridge.h"
#include <cstdio>#include <cstdlib>#include <dlfcn.h>#include <cmath>#include <memory>#include <mutex>#include <unordered_map>#include <vector>#include <atomic>
namespace {
constexpr size_t kMaximumBitmapBytes = 256u * 1024u * 1024u;constexpr size_t kMaximumColorComponents = 1024;constexpr size_t kMaximumGradientStops = 4096;constexpr size_t kMaximumGradientComponentsPerStop = 64;constexpr size_t kMaximumPathPoints = 1024u * 1024u;constexpr size_t kMaximumFontGlyphs = 1024u * 1024u;
struct BitmapBacking { CGContextRef context = nullptr; u32 guestData = 0; size_t guestBytesPerRow = 0; size_t hostBytesPerRow = 0; size_t height = 0; size_t guestByteCount = 0; std::unique_ptr<uint8_t[]> bytes;};
struct DataProviderBacking { std::vector<uint8_t> bytes; u32 guestInfo = 0; u32 guestData = 0; u32 guestSize = 0; u32 guestRelease = 0; std::atomic<bool> releaseCalled{false};};
void ReleaseDataProviderBacking(void *info, const void *, size_t) { std::unique_ptr<std::shared_ptr<DataProviderBacking>> owner( static_cast<std::shared_ptr<DataProviderBacking> *>(info)); const auto backing = *owner; backing->releaseCalled.store(true, std::memory_order_release); if(!backing->guestRelease) return; u32 arguments[] = { backing->guestInfo, backing->guestData, backing->guestSize, }; if(Dynarmic_guest_thread_is_registered()) { (void)LC32InvokeGuestC(backing->guestRelease, false, 3, arguments); return; } LC32GuestBlockCallbackDescriptor descriptor = {}; descriptor.kind = LC32GuestBlockCallbackKindFunction; descriptor.guestInvoke = backing->guestRelease; descriptor.argumentCount = 3; descriptor.resultKind = LC32GuestBlockValueVoid; for(size_t index = 0; index < 3; ++index) { descriptor.arguments[index].kind = LC32GuestBlockValueUnsigned32; descriptor.arguments[index].value = arguments[index]; } if(!Dynarmic_submit_guest_function_callback(&descriptor)) { std::fprintf(stderr, "LC32: could not deliver data-provider release callback\n"); }}
std::mutex bitmapBackingsMutex;std::unordered_map<CGContextRef, BitmapBacking *> bitmapBackings;
using CGContextGetFillColorSpaceFunction = CGColorSpaceRef (*)(CGContextRef);
CGContextGetFillColorSpaceFunction GetContextFillColorSpaceFunction() { static const auto function = reinterpret_cast<CGContextGetFillColorSpaceFunction>( dlsym(RTLD_DEFAULT, "CGContextGetFillColorSpace")); return function;}
void ReportMissingContextFillColorSpaceFunction() { static std::once_flag once; std::call_once(once, [] { std::fprintf(stderr, "LC32: CGContextSetFillColor is unavailable because the host " "does not export CGContextGetFillColorSpace\n"); });}
using CGContextGetStrokeColorSpaceFunction = CGColorSpaceRef (*)(CGContextRef);
CGContextGetStrokeColorSpaceFunction GetContextStrokeColorSpaceFunction() { static const auto function = reinterpret_cast<CGContextGetStrokeColorSpaceFunction>( dlsym(RTLD_DEFAULT, "CGContextGetStrokeColorSpace")); return function;}
void ReportMissingContextStrokeColorSpaceFunction() { static std::once_flag once; std::call_once(once, [] { std::fprintf(stderr, "LC32: CGContextSetStrokeColor is unavailable because the host " "does not export CGContextGetStrokeColorSpace\n"); });}
bool ReadCoreGraphicsCall(u32 guestAddress, LC32CoreGraphicsCall &call) { struct { uint32_t version; uint32_t slotCount; } header = {}; if(!guestAddress || Dynarmic_mem_1read(guestAddress, sizeof(header), reinterpret_cast<char *>(&header)) != 0 || header.version != LC32CoreGraphicsABIVersion || header.slotCount > LC32CoreGraphicsMaxSlots) { return false; }
call = {}; call.version = header.version; call.slotCount = header.slotCount; const size_t byteCount = header.slotCount * sizeof(call.slots[0]); const uint64_t slotsAddress = static_cast<uint64_t>(guestAddress) + offsetof(LC32CoreGraphicsCall, slots); if(slotsAddress > UINT32_MAX || slotsAddress + byteCount > static_cast<uint64_t>(UINT32_MAX) + 1) return false; if(byteCount && Dynarmic_mem_1read( static_cast<u32>(slotsAddress), byteCount, reinterpret_cast<char *>(call.slots)) != 0) { return false; } return true;}
bool RequireCoreGraphicsSlots(const LC32CoreGraphicsCall &call, uint32_t count) { return call.slotCount == count;}
u32 SlotU32(const LC32CoreGraphicsCall &call, size_t index) { return static_cast<u32>(call.slots[index]);}
template<typename T>T SlotHostObject(const LC32CoreGraphicsCall &call, size_t index) { return reinterpret_cast<T>( static_cast<uintptr_t>(call.slots[index]));}
CGFloat SlotCGFloat(const LC32CoreGraphicsCall &call, size_t index) { const uint32_t bits = SlotU32(call, index); float value; memcpy(&value, &bits, sizeof(value)); return static_cast<CGFloat>(value);}
u32 ReturnCGFloat(CGFloat value) { const float narrowed = static_cast<float>(value); u32 bits; memcpy(&bits, &narrowed, sizeof(bits)); return bits;}
bool ReadGuestCGFloatArray(u32 guestAddress, size_t count, bool nullable, std::vector<CGFloat> &hostValues, const CGFloat *&hostPointer) { hostPointer = nullptr; hostValues.clear(); if(!guestAddress) return nullable; if(!count || count > (kMaximumGradientComponentsPerStop + 1) * kMaximumGradientStops || count > SIZE_MAX / sizeof(float)) { return false; }
const size_t byteCount = count * sizeof(float); if(static_cast<uint64_t>(guestAddress) + byteCount > static_cast<uint64_t>(UINT32_MAX) + 1) { return false; }
std::vector<float> guestValues(count); if(Dynarmic_mem_1read(guestAddress, byteCount, reinterpret_cast<char *>(guestValues.data())) != 0) { return false; } hostValues.resize(count); for(size_t index = 0; index < count; ++index) hostValues[index] = static_cast<CGFloat>(guestValues[index]); hostPointer = hostValues.data(); return true;}
bool ReadGuestGradientLocations(u32 guestAddress, size_t count, std::vector<CGFloat> &hostValues, const CGFloat *&hostPointer) { if(!ReadGuestCGFloatArray(guestAddress, count, true, hostValues, hostPointer)) { return false; } if(!hostPointer) return true;
CGFloat previous = 0; for(size_t index = 0; index < count; ++index) { const CGFloat location = hostPointer[index]; if(!std::isfinite(location) || location < 0 || location > 1 || (index && location < previous)) { return false; } previous = location; } return true;}
bool ReadGuestPoints(u32 guestAddress, size_t count, std::vector<CGPoint> &hostPoints) { hostPoints.clear(); if(!guestAddress || !count || count > kMaximumPathPoints) { return false; }
const size_t componentCount = count * 2; const size_t byteCount = componentCount * sizeof(float); if(static_cast<uint64_t>(guestAddress) + byteCount > static_cast<uint64_t>(UINT32_MAX) + 1) { return false; }
std::vector<float> guestValues(componentCount); if(Dynarmic_mem_1read(guestAddress, byteCount, reinterpret_cast<char *>(guestValues.data())) != 0) { return false; }
hostPoints.resize(count); for(size_t index = 0; index < count; ++index) { hostPoints[index] = CGPointMake( static_cast<CGFloat>(guestValues[index * 2]), static_cast<CGFloat>(guestValues[index * 2 + 1])); } return true;}
bool WriteGuestRect(u32 guestAddress, CGRect rect) { if(!guestAddress || static_cast<uint64_t>(guestAddress) + 4 * sizeof(float) > static_cast<uint64_t>(UINT32_MAX) + 1) { return false; } float guestRect[] = { static_cast<float>(rect.origin.x), static_cast<float>(rect.origin.y), static_cast<float>(rect.size.width), static_cast<float>(rect.size.height), }; return Dynarmic_mem_1write(guestAddress, sizeof(guestRect), reinterpret_cast<char *>(guestRect)) == 0;}
CGRect SlotRect(const LC32CoreGraphicsCall &call, size_t first) { return CGRectMake(SlotCGFloat(call, first), SlotCGFloat(call, first + 1), SlotCGFloat(call, first + 2), SlotCGFloat(call, first + 3));}
bool SlotOptionalTransform(const LC32CoreGraphicsCall &call, size_t presenceIndex, size_t first, CGAffineTransform &storage, const CGAffineTransform *&transform) { const u32 present = SlotU32(call, presenceIndex); if(!present) { transform = nullptr; return true; } if(present != 1) return false; storage = CGAffineTransformMake( SlotCGFloat(call, first), SlotCGFloat(call, first + 1), SlotCGFloat(call, first + 2), SlotCGFloat(call, first + 3), SlotCGFloat(call, first + 4), SlotCGFloat(call, first + 5)); transform = &storage; return true;}
BitmapBacking *FindBitmapBacking(CGContextRef context) { std::lock_guard<std::mutex> lock(bitmapBackingsMutex); const auto iterator = bitmapBackings.find(context); return iterator == bitmapBackings.end() ? nullptr : iterator->second;}
void SyncBitmapBacking(CGContextRef context, BitmapBacking *backing) { if(!backing || !backing->guestData || !backing->guestByteCount) return; CGContextFlush(context); auto *data = static_cast<uint8_t *>(CGBitmapContextGetData(context)); if(!data) return; if(backing->guestBytesPerRow == backing->hostBytesPerRow) { (void)Dynarmic_mem_1write(backing->guestData, backing->guestByteCount, reinterpret_cast<char *>(data)); return; } for(size_t row = 0; row < backing->height; ++row) { const u32 guestRow = backing->guestData + static_cast<u32>(row * backing->guestBytesPerRow); (void)Dynarmic_mem_1write(guestRow, backing->guestBytesPerRow, reinterpret_cast<char *>(data + row * backing->hostBytesPerRow)); }}
void ReleaseBitmapBacking(void *releaseInfo, void *) { auto *backing = static_cast<BitmapBacking *>(releaseInfo); if(!backing) return;#ifdef LC32_TRACE_COREGRAPHICS { std::fprintf(stderr, "LC32 CG: release bitmap backing=%p context=%p guest=0x%08x bytes=%zu\n", backing, backing->context, backing->guestData, backing->guestByteCount); }#endif /* A failed CGBitmapContextCreateWithData can synchronously invoke the * release callback before returning NULL. The creator still owns the * bookkeeping object until it has observed that return value. */ if(!backing->context) return; /* * Do not copy pixels back here. Core Graphics invokes this callback only * while destroying the context, after the guest is allowed to have freed * the buffer supplied to CGBitmapContextCreate. Mutating operations and * CGBitmapContextGetData synchronize while that guest buffer is live. */ if(backing->context) { std::lock_guard<std::mutex> lock(bitmapBackingsMutex); const auto iterator = bitmapBackings.find(backing->context); if(iterator != bitmapBackings.end() && iterator->second == backing) bitmapBackings.erase(iterator); } delete backing;}
} // namespace
__BEGIN_DECLS
void LC32CoreGraphicsSyncBitmapBacking(CGContextRef context) { if(context) SyncBitmapBacking(context, FindBitmapBacking(context));}
u32 LC32_CoreGraphics_Dispatch(u32 opcode, u32 guestCall, u32) { LC32CoreGraphicsCall call; if(!ReadCoreGraphicsCall(guestCall, call)) return 0;
switch(static_cast<LC32CoreGraphicsOpcode>(opcode)) { case LC32CoreGraphicsOpBitmapContextCreate: { if(!RequireCoreGraphicsSlots(call, 7)) return 0; const size_t width = SlotU32(call, 1); const size_t height = SlotU32(call, 2); const size_t bytesPerRow = SlotU32(call, 4); if(height && bytesPerRow > kMaximumBitmapBytes / height) return 0; const size_t byteCount = bytesPerRow * height; if(byteCount > kMaximumBitmapBytes) return 0; const u32 guestData = SlotU32(call, 0);#ifdef LC32_TRACE_COREGRAPHICS { std::fprintf(stderr, "LC32 CG: bitmap create guest=0x%08x size=%zux%zu bpc=%u bpr=%zu cs=%p info=0x%08x bytes=%zu\n", guestData, width, height, SlotU32(call, 3), bytesPerRow, SlotHostObject<CGColorSpaceRef>(call, 5), SlotU32(call, 6), byteCount); }#endif if(guestData && static_cast<uint64_t>(guestData) + byteCount > static_cast<uint64_t>(UINT32_MAX) + 1) return 0;
BitmapBacking *backing = nullptr; void *hostData = nullptr; CGColorSpaceRef colorSpace = SlotHostObject<CGColorSpaceRef>(call, 5); size_t hostBytesPerRow = bytesPerRow; const u32 alphaInfo = SlotU32(call, 6) & static_cast<u32>(kCGBitmapAlphaInfoMask); const bool isEightBitRGBA = SlotU32(call, 3) == 8 && colorSpace && CGColorSpaceGetModel(colorSpace) == kCGColorSpaceModelRGB && alphaInfo >= kCGImageAlphaPremultipliedLast && alphaInfo <= kCGImageAlphaNoneSkipFirst && bytesPerRow >= width * 4; if(guestData && isEightBitRGBA && hostBytesPerRow % 16) { hostBytesPerRow = (hostBytesPerRow + 15) & ~size_t(15); } if(height && hostBytesPerRow > kMaximumBitmapBytes / height) return 0; const size_t hostByteCount = hostBytesPerRow * height; if(guestData) { backing = new BitmapBacking(); backing->guestData = guestData; backing->guestBytesPerRow = bytesPerRow; backing->hostBytesPerRow = hostBytesPerRow; backing->height = height; backing->guestByteCount = byteCount; if(hostByteCount) { backing->bytes = std::make_unique<uint8_t[]>(hostByteCount); memset(backing->bytes.get(), 0, hostByteCount); bool copied = true; for(size_t row = 0; row < height; ++row) { const u32 guestRow = guestData + static_cast<u32>(row * bytesPerRow); if(Dynarmic_mem_1read(guestRow, bytesPerRow, reinterpret_cast<char *>(backing->bytes.get() + row * hostBytesPerRow)) != 0) { copied = false; break; } } if(!copied) { delete backing; return 0; } hostData = backing->bytes.get(); } }
CGContextRef context = backing ? CGBitmapContextCreateWithData(hostData, width, height, SlotU32(call, 3), hostBytesPerRow, colorSpace, SlotU32(call, 6), ReleaseBitmapBacking, backing) : CGBitmapContextCreate(nullptr, width, height, SlotU32(call, 3), bytesPerRow, colorSpace, SlotU32(call, 6));#ifdef LC32_TRACE_COREGRAPHICS { std::fprintf(stderr, "LC32 CG: bitmap result context=%p backing=%p guest-bpr=%zu host-bpr=%zu\n", context, backing, bytesPerRow, hostBytesPerRow); }#endif if(!context) { delete backing; return 0; } if(backing) { backing->context = context; std::lock_guard<std::mutex> lock(bitmapBackingsMutex); bitmapBackings.emplace(context, backing); } return LC32GuestObjectForOwnedHostObject(context); } case LC32CoreGraphicsOpColorSpaceCreateDeviceRGB: { if(!RequireCoreGraphicsSlots(call, 0)) return 0; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); if(!colorSpace) return 0; return LC32GuestObjectForOwnedHostObject(colorSpace); } case LC32CoreGraphicsOpColorSpaceCreateWithName: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CFStringRef name = SlotHostObject<CFStringRef>(call, 0); CGColorSpaceRef colorSpace = name ? CGColorSpaceCreateWithName(name) : nullptr; return colorSpace ? LC32GuestObjectForOwnedHostObject(colorSpace) : 0; } case LC32CoreGraphicsOpColorSpaceRelease: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; return 0; } case LC32CoreGraphicsOpContextClearRect: { if(!RequireCoreGraphicsSlots(call, 5)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextClearRect(context, SlotRect(call, 1)); SyncBitmapBacking(context, FindBitmapBacking(context)); return 0; } case LC32CoreGraphicsOpContextDrawImage: case LC32CoreGraphicsOpContextDrawTiledImage: { if(!RequireCoreGraphicsSlots(call, 6)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGImageRef image = SlotHostObject<CGImageRef>(call, 5); if(!context || !image) return 0; if(opcode == LC32CoreGraphicsOpContextDrawTiledImage) CGContextDrawTiledImage(context, SlotRect(call, 1), image); else CGContextDrawImage(context, SlotRect(call, 1), image); SyncBitmapBacking(context, FindBitmapBacking(context)); return 0; } case LC32CoreGraphicsOpContextRelease: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; /* The guest backing may already have been freed before release. */ return 0; } case LC32CoreGraphicsOpContextTranslateCTM: { if(!RequireCoreGraphicsSlots(call, 3)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(context) CGContextTranslateCTM(context, SlotCGFloat(call, 1), SlotCGFloat(call, 2)); return 0; } case LC32CoreGraphicsOpImageGetHeight: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); return image ? static_cast<u32>(CGImageGetHeight(image)) : 0; } case LC32CoreGraphicsOpImageGetWidth: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); return image ? static_cast<u32>(CGImageGetWidth(image)) : 0; } case LC32CoreGraphicsOpImageRelease: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; return 0; } case LC32CoreGraphicsOpColorGetColorSpace: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGColorRef color = SlotHostObject<CGColorRef>(call, 0); CGColorSpaceRef space = color ? CGColorGetColorSpace(color) : nullptr; return space ? [(id)space guest_self] : 0; } case LC32CoreGraphicsOpColorGetNumberOfComponents: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGColorRef color = SlotHostObject<CGColorRef>(call, 0); if(!color) return 0; const size_t count = CGColorGetNumberOfComponents(color); return count <= kMaximumColorComponents ? static_cast<u32>(count) : 0; } case LC32CoreGraphicsOpColorCopyComponents: { if(!RequireCoreGraphicsSlots(call, 3)) return 0; CGColorRef color = SlotHostObject<CGColorRef>(call, 0); const u32 guestComponents = SlotU32(call, 1); const size_t capacity = SlotU32(call, 2); if(!color || !guestComponents || capacity > kMaximumColorComponents) return 0;
const size_t count = CGColorGetNumberOfComponents(color); const CGFloat *components = CGColorGetComponents(color); if(!components || count == 0 || count > capacity || count > kMaximumColorComponents || static_cast<uint64_t>(guestComponents) + count * sizeof(float) > static_cast<uint64_t>(UINT32_MAX) + 1) { return 0; } std::vector<float> guestValues(count); for(size_t index = 0; index < count; ++index) guestValues[index] = static_cast<float>(components[index]); return Dynarmic_mem_1write(guestComponents, guestValues.size() * sizeof(guestValues[0]), reinterpret_cast<char *>(guestValues.data())) == 0; } case LC32CoreGraphicsOpColorSpaceGetModel: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGColorSpaceRef space = SlotHostObject<CGColorSpaceRef>(call, 0); return static_cast<u32>(space ? CGColorSpaceGetModel(space) : kCGColorSpaceModelUnknown); } case LC32CoreGraphicsOpContextSetAlpha: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextSetAlpha(context, SlotCGFloat(call, 1)); return 1; } case LC32CoreGraphicsOpColorSpaceGetNumberOfComponents: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGColorSpaceRef space = SlotHostObject<CGColorSpaceRef>(call, 0); return space ? static_cast<u32>(CGColorSpaceGetNumberOfComponents(space)) : 0; } case LC32CoreGraphicsOpDataProviderCreateWithFilename: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; const u32 guestFilename = SlotU32(call, 0); const size_t length = SlotU32(call, 1); if(!guestFilename || length > LC32CoreGraphicsMaximumFilenameBytes || static_cast<uint64_t>(guestFilename) + length + 1 > static_cast<uint64_t>(UINT32_MAX) + 1) { return 0; }
std::vector<char> filename(length + 1); if(Dynarmic_mem_1read(guestFilename, filename.size(), filename.data()) != 0 || filename[length] != '\0' || memchr(filename.data(), '\0', length) != nullptr) { return 0; }
CGDataProviderRef provider = CGDataProviderCreateWithFilename(filename.data()); if(!provider) return 0; return LC32GuestObjectForOwnedHostObject(provider); } case LC32CoreGraphicsOpDataProviderCreateWithCFData: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CFDataRef data = SlotHostObject<CFDataRef>(call, 0); if(!data || CFGetTypeID(data) != CFDataGetTypeID()) return 0; CGDataProviderRef provider = CGDataProviderCreateWithCFData(data); return provider ? LC32GuestObjectForOwnedHostObject(provider) : 0; } case LC32CoreGraphicsOpDataProviderCreateWithData: { if(!RequireCoreGraphicsSlots(call, 4)) return 0; const u32 guestData = SlotU32(call, 1); const size_t size = SlotU32(call, 2); if(size > kMaximumBitmapBytes || (size && !guestData) || static_cast<uint64_t>(guestData) + size > static_cast<uint64_t>(UINT32_MAX) + 1) return 0; auto backing = std::make_shared<DataProviderBacking>(); backing->bytes.resize(size ? size : 1); if(size && Dynarmic_mem_1read(guestData, size, reinterpret_cast<char *>(backing->bytes.data())) != 0) return 0; backing->guestInfo = SlotU32(call, 0); backing->guestData = guestData; backing->guestSize = static_cast<u32>(size); backing->guestRelease = SlotU32(call, 3); auto *owner = new std::shared_ptr<DataProviderBacking>(backing); CGDataProviderRef provider = CGDataProviderCreateWithData(owner, backing->bytes.data(), size, ReleaseDataProviderBacking); /* CoreGraphics may invoke its release callback synchronously * when creation fails. The creator keeps its own reference so * either failure path can dispose the backing exactly once. */ if(!provider && !backing->releaseCalled.load(std::memory_order_acquire)) delete owner; return provider ? LC32GuestObjectForOwnedHostObject(provider) : 0; } case LC32CoreGraphicsOpDataProviderRelease: case LC32CoreGraphicsOpDataProviderRetain: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGDataProviderRef provider = SlotHostObject<CGDataProviderRef>(call, 0); if(!provider || CFGetTypeID(provider) != CGDataProviderGetTypeID()) return 0; if(opcode == LC32CoreGraphicsOpDataProviderRetain) return LC32GuestObjectForOwnedHostObject( CGDataProviderRetain(provider)); CGDataProviderRelease(provider); return 0; } case LC32CoreGraphicsOpDataProviderCopyData: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGDataProviderRef provider = SlotHostObject<CGDataProviderRef>(call, 0); if(!provider || CFGetTypeID(provider) != CGDataProviderGetTypeID()) return 0; CFDataRef data = CGDataProviderCopyData(provider); return data ? LC32GuestObjectForOwnedHostObject(data) : 0; } case LC32CoreGraphicsOpFontCreateWithDataProvider: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGDataProviderRef provider = SlotHostObject<CGDataProviderRef>(call, 0); if(!provider || CFGetTypeID(provider) != CGDataProviderGetTypeID()) return 0; CGFontRef font = CGFontCreateWithDataProvider(provider); return font ? LC32GuestObjectForOwnedHostObject(font) : 0; } case LC32CoreGraphicsOpFontCopyTableForTag: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGFontRef font = SlotHostObject<CGFontRef>(call, 0); if(!font || CFGetTypeID(font) != CGFontGetTypeID()) return 0; CFDataRef table = CGFontCopyTableForTag(font, SlotU32(call, 1)); if(!table) return 0; /* CoreGraphics can return a no-copy view into the font's mapped * provider. Keep the guest's Copy result valid even after the * guest releases its font before accessing the table bytes. */ CFDataRef data = CFDataCreate(kCFAllocatorDefault, CFDataGetBytePtr(table), CFDataGetLength(table)); CFRelease(table); return data ? LC32GuestObjectForOwnedHostObject(data) : 0; } case LC32CoreGraphicsOpFontCreateWithFontName: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CFStringRef name = SlotHostObject<CFStringRef>(call, 0); CGFontRef font = name ? CGFontCreateWithFontName(name) : nullptr; return font ? LC32GuestObjectForOwnedHostObject(font) : 0; } case LC32CoreGraphicsOpFontGetItalicAngle: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGFontRef font = SlotHostObject<CGFontRef>(call, 0); return font ? ReturnCGFloat(CGFontGetItalicAngle(font)) : 0; } case LC32CoreGraphicsOpFontGetUnitsPerEm: case LC32CoreGraphicsOpFontGetAscent: case LC32CoreGraphicsOpFontGetDescent: case LC32CoreGraphicsOpFontGetCapHeight: case LC32CoreGraphicsOpFontGetLeading: case LC32CoreGraphicsOpFontGetNumberOfGlyphs: case LC32CoreGraphicsOpFontGetXHeight: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGFontRef font = SlotHostObject<CGFontRef>(call, 0); if(!font || CFGetTypeID(font) != CGFontGetTypeID()) return 0; int value; switch(opcode) { case LC32CoreGraphicsOpFontGetUnitsPerEm: value = CGFontGetUnitsPerEm(font); break; case LC32CoreGraphicsOpFontGetAscent: value = CGFontGetAscent(font); break; case LC32CoreGraphicsOpFontGetDescent: value = CGFontGetDescent(font); break; case LC32CoreGraphicsOpFontGetCapHeight: value = CGFontGetCapHeight(font); break; case LC32CoreGraphicsOpFontGetLeading: value = CGFontGetLeading(font); break; case LC32CoreGraphicsOpFontGetNumberOfGlyphs: return static_cast<u32>(CGFontGetNumberOfGlyphs(font)); default: value = CGFontGetXHeight(font); break; } return static_cast<u32>(value); } case LC32CoreGraphicsOpFontGetGlyphAdvances: case LC32CoreGraphicsOpFontGetGlyphBBoxes: { if(!RequireCoreGraphicsSlots(call, 4)) return 0; CGFontRef font = SlotHostObject<CGFontRef>(call, 0); const u32 guestGlyphs = SlotU32(call, 1); const size_t count = SlotU32(call, 2); const u32 guestAdvances = SlotU32(call, 3); if(!font || CFGetTypeID(font) != CGFontGetTypeID() || count > kMaximumFontGlyphs) return 0; if(!count) return 1; static_assert(sizeof(CGGlyph) == 2 && sizeof(int) == 4, "CGFont glyphs and advances must match the ARM32 ABI"); const size_t glyphBytes = count * sizeof(CGGlyph); const bool wantsBounds = opcode == LC32CoreGraphicsOpFontGetGlyphBBoxes; const size_t advanceBytes = count * (wantsBounds ? 4 * sizeof(float) : sizeof(int)); if(!guestGlyphs || !guestAdvances || static_cast<uint64_t>(guestGlyphs) + glyphBytes > static_cast<uint64_t>(UINT32_MAX) + 1 || static_cast<uint64_t>(guestAdvances) + advanceBytes > static_cast<uint64_t>(UINT32_MAX) + 1) return 0;
std::vector<CGGlyph> glyphs(count); if(Dynarmic_mem_1read(guestGlyphs, glyphBytes, reinterpret_cast<char *>(glyphs.data())) != 0) return 0; if(wantsBounds) { std::vector<CGRect> bounds(count); if(!CGFontGetGlyphBBoxes(font, glyphs.data(), count, bounds.data())) return 0; std::vector<float> narrowed(count * 4); for(size_t i = 0; i < count; ++i) { narrowed[i * 4] = bounds[i].origin.x; narrowed[i * 4 + 1] = bounds[i].origin.y; narrowed[i * 4 + 2] = bounds[i].size.width; narrowed[i * 4 + 3] = bounds[i].size.height; } return Dynarmic_mem_1write(guestAdvances, advanceBytes, reinterpret_cast<char *>(narrowed.data())) == 0; } std::vector<int> advances(count); if(!CGFontGetGlyphAdvances(font, glyphs.data(), count, advances.data())) return 0; return Dynarmic_mem_1write(guestAdvances, advanceBytes, reinterpret_cast<char *>(advances.data())) == 0; } case LC32CoreGraphicsOpImageCreate: { if(!RequireCoreGraphicsSlots(call, 11)) return 0; const size_t width = SlotU32(call, 0); const size_t height = SlotU32(call, 1); const size_t bitsPerComponent = SlotU32(call, 2); const size_t bitsPerPixel = SlotU32(call, 3); const size_t bytesPerRow = SlotU32(call, 4); CGColorSpaceRef space = SlotHostObject<CGColorSpaceRef>(call, 5); CGDataProviderRef provider = SlotHostObject<CGDataProviderRef>(call, 7); const u32 guestDecode = SlotU32(call, 8); const u32 shouldInterpolate = SlotU32(call, 9); const u32 intentValue = SlotU32(call, 10);
if(!width || !height || !bitsPerComponent || !bitsPerPixel || !bytesPerRow || !provider || shouldInterpolate > 1 || intentValue > kCGRenderingIntentSaturation || CFGetTypeID(provider) != CGDataProviderGetTypeID() || (space && CFGetTypeID(space) != CGColorSpaceGetTypeID())) { return 0; }
/* Keep pathological guest metadata from making CoreGraphics * reserve an unbounded image. The row must contain every pixel, * and the represented backing remains within the same limit as * the bitmap-context bridge. */ if(width > (SIZE_MAX - 7) / bitsPerPixel) return 0; const size_t minimumBytesPerRow = (width * bitsPerPixel + 7) / 8; if(bytesPerRow < minimumBytesPerRow || bytesPerRow > kMaximumBitmapBytes / height) { return 0; }
std::vector<CGFloat> decodeValues; const CGFloat *decode = nullptr; if(guestDecode) { if(!space) return 0; const size_t componentCount = CGColorSpaceGetNumberOfComponents(space); if(!componentCount || componentCount > kMaximumColorComponents || componentCount > SIZE_MAX / 2 || !ReadGuestCGFloatArray(guestDecode, componentCount * 2, false, decodeValues, decode)) { return 0; } }
CGImageRef image = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, space, static_cast<CGBitmapInfo>(SlotU32(call, 6)), provider, decode, shouldInterpolate != 0, static_cast<CGColorRenderingIntent>(intentValue)); return image ? LC32GuestObjectForOwnedHostObject(image) : 0; } case LC32CoreGraphicsOpImageCreateWithJPEGDataProvider: case LC32CoreGraphicsOpImageCreateWithPNGDataProvider: { if(!RequireCoreGraphicsSlots(call, 4)) return 0; CGDataProviderRef provider = SlotHostObject<CGDataProviderRef>(call, 0); /* A CGFloat decode array has no length in this API. Never pass * the guest address to CoreGraphics; reject it until the required * component count can be established safely. */ if(!provider || SlotU32(call, 1) != 0) return 0;
const bool shouldInterpolate = SlotU32(call, 2) != 0; const auto intent = static_cast<CGColorRenderingIntent>( SlotU32(call, 3)); CGImageRef image = static_cast<LC32CoreGraphicsOpcode>(opcode) == LC32CoreGraphicsOpImageCreateWithJPEGDataProvider ? CGImageCreateWithJPEGDataProvider(provider, nullptr, shouldInterpolate, intent) : CGImageCreateWithPNGDataProvider(provider, nullptr, shouldInterpolate, intent); if(!image) return 0; return LC32GuestObjectForOwnedHostObject(image); } case LC32CoreGraphicsOpPathCreateMutable: { if(!RequireCoreGraphicsSlots(call, 0)) return 0; CGMutablePathRef path = CGPathCreateMutable(); if(!path) return 0; /* CGPathCreateMutable returns +1. Keep that ownership paired with * the +1 guest proxy returned by -guest_self. */ return LC32GuestObjectForOwnedHostObject(path); } case LC32CoreGraphicsOpPathAddLineToPoint: case LC32CoreGraphicsOpPathMoveToPoint: { if(!RequireCoreGraphicsSlots(call, 10)) return 0; CGMutablePathRef path = SlotHostObject<CGMutablePathRef>(call, 0); if(!path) return 0; CGAffineTransform transformStorage; const CGAffineTransform *transform; if(!SlotOptionalTransform(call, 1, 2, transformStorage, transform)) return 0; if(static_cast<LC32CoreGraphicsOpcode>(opcode) == LC32CoreGraphicsOpPathAddLineToPoint) { CGPathAddLineToPoint(path, transform, SlotCGFloat(call, 8), SlotCGFloat(call, 9)); } else { CGPathMoveToPoint(path, transform, SlotCGFloat(call, 8), SlotCGFloat(call, 9)); } return 1; } case LC32CoreGraphicsOpPathContainsPoint: { if(!RequireCoreGraphicsSlots(call, 11)) return 0; CGPathRef path = SlotHostObject<CGPathRef>(call, 0); if(!path) return 0; CGAffineTransform transformStorage; const CGAffineTransform *transform; if(!SlotOptionalTransform(call, 1, 2, transformStorage, transform)) return 0; const CGPoint point = CGPointMake( SlotCGFloat(call, 8), SlotCGFloat(call, 9)); return CGPathContainsPoint(path, transform, point, SlotU32(call, 10) != 0); } case LC32CoreGraphicsOpPathCloseSubpath: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGMutablePathRef path = SlotHostObject<CGMutablePathRef>(call, 0); if(!path) return 0; CGPathCloseSubpath(path); return 1; } case LC32CoreGraphicsOpPathRelease: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; /* Guest CFRelease performs the paired proxy/native decrement. * This opcode deliberately validates without releasing again. */ return SlotHostObject<CGPathRef>(call, 0) != nullptr; } case LC32CoreGraphicsOpColorCreate: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGColorSpaceRef space = SlotHostObject<CGColorSpaceRef>(call, 0); const u32 guestComponents = SlotU32(call, 1); if(!space || !guestComponents) return 0;
const size_t colorComponents = CGColorSpaceGetNumberOfComponents(space); if(colorComponents >= kMaximumColorComponents) return 0; const size_t count = colorComponents + 1; const size_t byteCount = count * sizeof(float); if(static_cast<uint64_t>(guestComponents) + byteCount > static_cast<uint64_t>(UINT32_MAX) + 1) { return 0; }
std::vector<float> guestValues(count); if(Dynarmic_mem_1read(guestComponents, byteCount, reinterpret_cast<char *>(guestValues.data())) != 0) { return 0; } std::vector<CGFloat> hostValues(count); for(size_t index = 0; index < count; ++index) hostValues[index] = guestValues[index]; CGColorRef color = CGColorCreate(space, hostValues.data()); return color ? LC32GuestObjectForOwnedHostObject(color) : 0; } case LC32CoreGraphicsOpColorEqualToColor: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGColorRef color1 = SlotHostObject<CGColorRef>(call, 0); CGColorRef color2 = SlotHostObject<CGColorRef>(call, 1); return CGColorEqualToColor(color1, color2); } case LC32CoreGraphicsOpColorGetAlpha: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGColorRef color = SlotHostObject<CGColorRef>(call, 0); return color ? ReturnCGFloat(CGColorGetAlpha(color)) : 0; } case LC32CoreGraphicsOpColorSpaceCreateDeviceGray: { if(!RequireCoreGraphicsSlots(call, 0)) return 0; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); return colorSpace ? LC32GuestObjectForOwnedHostObject(colorSpace) : 0; } case LC32CoreGraphicsOpGradientCreateWithColorComponents: { if(!RequireCoreGraphicsSlots(call, 4)) return 0; CGColorSpaceRef space = SlotHostObject<CGColorSpaceRef>(call, 0); const size_t stopCount = SlotU32(call, 3); if(!stopCount || stopCount > kMaximumGradientStops) return 0;
/* CoreGraphics uses Generic RGB when the space is NULL. Every * stop contains the color-space components followed by alpha. */ size_t colorComponentCount = space ? CGColorSpaceGetNumberOfComponents(space) : 3; if(!colorComponentCount || colorComponentCount > kMaximumGradientComponentsPerStop || colorComponentCount + 1 > SIZE_MAX / stopCount) { return 0; } if(space) { const CGColorSpaceModel model = CGColorSpaceGetModel(space); if(model == kCGColorSpaceModelIndexed || model == kCGColorSpaceModelPattern) { return 0; } }
const size_t componentCount = (colorComponentCount + 1) * stopCount; std::vector<CGFloat> components; const CGFloat *componentPointer; if(!ReadGuestCGFloatArray(SlotU32(call, 1), componentCount, false, components, componentPointer)) { return 0; } std::vector<CGFloat> locations; const CGFloat *locationPointer; if(!ReadGuestGradientLocations(SlotU32(call, 2), stopCount, locations, locationPointer)) { return 0; }
CGGradientRef gradient = CGGradientCreateWithColorComponents( space, componentPointer, locationPointer, stopCount); return gradient ? LC32GuestObjectForOwnedHostObject(gradient) : 0; } case LC32CoreGraphicsOpGradientCreateWithColors: { if(!RequireCoreGraphicsSlots(call, 3)) return 0; CGColorSpaceRef space = SlotHostObject<CGColorSpaceRef>(call, 0); CFArrayRef colors = SlotHostObject<CFArrayRef>(call, 1); if(!colors || CFGetTypeID(colors) != CFArrayGetTypeID()) return 0; const CFIndex signedCount = CFArrayGetCount(colors); if(signedCount <= 0 || static_cast<uint64_t>(signedCount) > kMaximumGradientStops) { return 0; } const size_t stopCount = static_cast<size_t>(signedCount); for(CFIndex index = 0; index < signedCount; ++index) { CFTypeRef color = static_cast<CFTypeRef>( CFArrayGetValueAtIndex(colors, index)); if(!color || CFGetTypeID(color) != CGColorGetTypeID()) return 0; }
std::vector<CGFloat> locations; const CGFloat *locationPointer; if(!ReadGuestGradientLocations(SlotU32(call, 2), stopCount, locations, locationPointer)) { return 0; } CGGradientRef gradient = CGGradientCreateWithColors( space, colors, locationPointer); return gradient ? LC32GuestObjectForOwnedHostObject(gradient) : 0; } case LC32CoreGraphicsOpBitmapContextCreateImage: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGImageRef image = CGBitmapContextCreateImage(context); return image ? LC32GuestObjectForOwnedHostObject(image) : 0; } case LC32CoreGraphicsOpBitmapContextGetWidth: case LC32CoreGraphicsOpBitmapContextGetHeight: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; return static_cast<u32>(opcode == LC32CoreGraphicsOpBitmapContextGetWidth ? CGBitmapContextGetWidth(context) : CGBitmapContextGetHeight(context)); } case LC32CoreGraphicsOpBitmapContextGetBytesPerRow: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); BitmapBacking *backing = context ? FindBitmapBacking(context) : nullptr; return backing ? static_cast<u32>(backing->guestBytesPerRow) : context ? static_cast<u32>(CGBitmapContextGetBytesPerRow(context)) : 0; } case LC32CoreGraphicsOpBitmapContextGetData: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); BitmapBacking *backing = context ? FindBitmapBacking(context) : nullptr; if(context && backing) SyncBitmapBacking(context, backing); return backing ? backing->guestData : 0; } case LC32CoreGraphicsOpContextSetFont: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGFontRef font = SlotHostObject<CGFontRef>(call, 1); if(context && font && CFGetTypeID(font) == CGFontGetTypeID()) CGContextSetFont(context, font); return 0; } case LC32CoreGraphicsOpContextSetFontSize: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(context) CGContextSetFontSize(context, SlotCGFloat(call, 1)); return 0; } case LC32CoreGraphicsOpContextSetTextDrawingMode: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 mode = SlotU32(call, 1); if(context && mode <= kCGTextClip) CGContextSetTextDrawingMode(context, static_cast<CGTextDrawingMode>(mode)); return 0; } case LC32CoreGraphicsOpContextShowGlyphsAtPoint: { if(!RequireCoreGraphicsSlots(call, 5)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 guestGlyphs = SlotU32(call, 3); const size_t count = SlotU32(call, 4); if(!context || !guestGlyphs || !count || count > kMaximumFontGlyphs || static_cast<uint64_t>(guestGlyphs) + count * sizeof(CGGlyph) > static_cast<uint64_t>(UINT32_MAX) + 1) return 0; std::vector<CGGlyph> glyphs(count); if(Dynarmic_mem_1read(guestGlyphs, count * sizeof(CGGlyph), reinterpret_cast<char *>(glyphs.data())) != 0) return 0;#pragma clang diagnostic push#pragma clang diagnostic ignored "-Wdeprecated-declarations" CGContextShowGlyphsAtPoint(context, SlotCGFloat(call, 1), SlotCGFloat(call, 2), glyphs.data(), count);#pragma clang diagnostic pop SyncBitmapBacking(context, FindBitmapBacking(context)); return 1; } case LC32CoreGraphicsOpContextShowGlyphsAtPositions: { if(!RequireCoreGraphicsSlots(call, 4)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 guestGlyphs = SlotU32(call, 1); const size_t count = SlotU32(call, 3); std::vector<CGPoint> positions; if(!context || !guestGlyphs || count > kMaximumFontGlyphs || static_cast<uint64_t>(guestGlyphs) + count * sizeof(CGGlyph) > static_cast<uint64_t>(UINT32_MAX) + 1 || !ReadGuestPoints(SlotU32(call, 2), count, positions)) return 0; std::vector<CGGlyph> glyphs(count); if(Dynarmic_mem_1read(guestGlyphs, count * sizeof(CGGlyph), reinterpret_cast<char *>(glyphs.data())) != 0) return 0; CGContextShowGlyphsAtPositions(context, glyphs.data(), positions.data(), count); SyncBitmapBacking(context, FindBitmapBacking(context)); return 1; } case LC32CoreGraphicsOpContextBeginTransparencyLayer: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CFDictionaryRef auxiliaryInfo = SlotHostObject<CFDictionaryRef>(call, 1); if(!context || (auxiliaryInfo && CFGetTypeID(auxiliaryInfo) != CFDictionaryGetTypeID())) return 0; CGContextBeginTransparencyLayer(context, auxiliaryInfo); return 1; } case LC32CoreGraphicsOpContextEndTransparencyLayer: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextEndTransparencyLayer(context); SyncBitmapBacking(context, FindBitmapBacking(context)); return 1; } case LC32CoreGraphicsOpContextSaveGState: case LC32CoreGraphicsOpContextRestoreGState: case LC32CoreGraphicsOpContextBeginPath: case LC32CoreGraphicsOpContextClosePath: case LC32CoreGraphicsOpContextClip: case LC32CoreGraphicsOpContextFlush: case LC32CoreGraphicsOpContextFillPath: case LC32CoreGraphicsOpContextStrokePath: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; switch(static_cast<LC32CoreGraphicsOpcode>(opcode)) { case LC32CoreGraphicsOpContextSaveGState: CGContextSaveGState(context); break; case LC32CoreGraphicsOpContextRestoreGState: CGContextRestoreGState(context); break; case LC32CoreGraphicsOpContextBeginPath: CGContextBeginPath(context); break; case LC32CoreGraphicsOpContextClosePath: CGContextClosePath(context); break; case LC32CoreGraphicsOpContextClip: CGContextClip(context); break; case LC32CoreGraphicsOpContextFlush: CGContextFlush(context); SyncBitmapBacking(context, FindBitmapBacking(context)); break; case LC32CoreGraphicsOpContextFillPath: CGContextFillPath(context); SyncBitmapBacking(context, FindBitmapBacking(context)); break; case LC32CoreGraphicsOpContextStrokePath: CGContextStrokePath(context); SyncBitmapBacking(context, FindBitmapBacking(context)); break; default: break; } return 1; } case LC32CoreGraphicsOpContextDrawPath: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextDrawPath(context, static_cast<CGPathDrawingMode>(SlotU32(call, 1))); SyncBitmapBacking(context, FindBitmapBacking(context)); return 1; } case LC32CoreGraphicsOpContextMoveToPoint: case LC32CoreGraphicsOpContextAddLineToPoint: case LC32CoreGraphicsOpContextScaleCTM: case LC32CoreGraphicsOpContextSetGrayFillColor: case LC32CoreGraphicsOpContextSetGrayStrokeColor: case LC32CoreGraphicsOpContextSetTextPosition: { if(!RequireCoreGraphicsSlots(call, 3)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; const CGFloat first = SlotCGFloat(call, 1); const CGFloat second = SlotCGFloat(call, 2); switch(static_cast<LC32CoreGraphicsOpcode>(opcode)) { case LC32CoreGraphicsOpContextMoveToPoint: CGContextMoveToPoint(context, first, second); break; case LC32CoreGraphicsOpContextAddLineToPoint: CGContextAddLineToPoint(context, first, second); break; case LC32CoreGraphicsOpContextScaleCTM: CGContextScaleCTM(context, first, second); break; case LC32CoreGraphicsOpContextSetGrayFillColor: CGContextSetGrayFillColor(context, first, second); break; case LC32CoreGraphicsOpContextSetGrayStrokeColor: CGContextSetGrayStrokeColor(context, first, second); break; case LC32CoreGraphicsOpContextSetTextPosition: CGContextSetTextPosition(context, first, second); break; default: break; } return 1; } case LC32CoreGraphicsOpContextGetTextPosition: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 guestAddress = SlotU32(call, 1); if(!context || !guestAddress || static_cast<uint64_t>(guestAddress) + 2 * sizeof(float) > static_cast<uint64_t>(UINT32_MAX) + 1) return 0; const CGPoint position = CGContextGetTextPosition(context); float guestPosition[] = { static_cast<float>(position.x), static_cast<float>(position.y), }; return Dynarmic_mem_1write(guestAddress, sizeof(guestPosition), reinterpret_cast<char *>(guestPosition)) == 0; } case LC32CoreGraphicsOpContextAddLines: { if(!RequireCoreGraphicsSlots(call, 3)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); std::vector<CGPoint> points; if(!context || !ReadGuestPoints(SlotU32(call, 1), SlotU32(call, 2), points)) { return 0; } CGContextAddLines(context, points.data(), points.size()); return 1; } case LC32CoreGraphicsOpContextRotateCTM: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(context) CGContextRotateCTM(context, SlotCGFloat(call, 1)); return context ? 1 : 0; } case LC32CoreGraphicsOpContextAddArc: { if(!RequireCoreGraphicsSlots(call, 7)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextAddArc(context, SlotCGFloat(call, 1), SlotCGFloat(call, 2), SlotCGFloat(call, 3), SlotCGFloat(call, 4), SlotCGFloat(call, 5), SlotU32(call, 6) != 0); return 1; } case LC32CoreGraphicsOpContextAddArcToPoint: { if(!RequireCoreGraphicsSlots(call, 6)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextAddArcToPoint(context, SlotCGFloat(call, 1), SlotCGFloat(call, 2), SlotCGFloat(call, 3), SlotCGFloat(call, 4), SlotCGFloat(call, 5)); return 1; } case LC32CoreGraphicsOpContextAddRect: case LC32CoreGraphicsOpContextFillRect: case LC32CoreGraphicsOpContextStrokeRect: { if(!RequireCoreGraphicsSlots(call, 5)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; const CGRect rect = SlotRect(call, 1); switch(static_cast<LC32CoreGraphicsOpcode>(opcode)) { case LC32CoreGraphicsOpContextAddRect: CGContextAddRect(context, rect); break; case LC32CoreGraphicsOpContextFillRect: CGContextFillRect(context, rect); SyncBitmapBacking(context, FindBitmapBacking(context)); break; case LC32CoreGraphicsOpContextStrokeRect: CGContextStrokeRect(context, rect); SyncBitmapBacking(context, FindBitmapBacking(context)); break; default: break; } return 1; } case LC32CoreGraphicsOpContextAddPath: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGPathRef path = SlotHostObject<CGPathRef>(call, 1); if(!context || !path) return 0; CGContextAddPath(context, path); return 1; } case LC32CoreGraphicsOpContextClipToMask: { if(!RequireCoreGraphicsSlots(call, 6)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGImageRef mask = SlotHostObject<CGImageRef>(call, 5); if(!context || !mask) return 0; CGContextClipToMask(context, SlotRect(call, 1), mask); return 1; } case LC32CoreGraphicsOpContextConcatCTM: { if(!RequireCoreGraphicsSlots(call, 7)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextConcatCTM(context, CGAffineTransformMake( SlotCGFloat(call, 1), SlotCGFloat(call, 2), SlotCGFloat(call, 3), SlotCGFloat(call, 4), SlotCGFloat(call, 5), SlotCGFloat(call, 6))); return 1; } case LC32CoreGraphicsOpContextDrawLinearGradient: { if(!RequireCoreGraphicsSlots(call, 7)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGGradientRef gradient = SlotHostObject<CGGradientRef>(call, 1); const u32 options = SlotU32(call, 6); const u32 validOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; if(!context || !gradient || (options & ~validOptions)) return 0; CGContextDrawLinearGradient(context, gradient, CGPointMake(SlotCGFloat(call, 2), SlotCGFloat(call, 3)), CGPointMake(SlotCGFloat(call, 4), SlotCGFloat(call, 5)), static_cast<CGGradientDrawingOptions>(options)); SyncBitmapBacking(context, FindBitmapBacking(context)); return 1; } case LC32CoreGraphicsOpContextDrawRadialGradient: { if(!RequireCoreGraphicsSlots(call, 9)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGGradientRef gradient = SlotHostObject<CGGradientRef>(call, 1); if(!context || !gradient) return 0; const u32 options = SlotU32(call, 8); const u32 validOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation; if(options & ~validOptions) return 0; CGContextDrawRadialGradient(context, gradient, CGPointMake(SlotCGFloat(call, 2), SlotCGFloat(call, 3)), SlotCGFloat(call, 4), CGPointMake(SlotCGFloat(call, 5), SlotCGFloat(call, 6)), SlotCGFloat(call, 7), static_cast<CGGradientDrawingOptions>(options)); SyncBitmapBacking(context, FindBitmapBacking(context)); return 1; } case LC32CoreGraphicsOpContextAddEllipseInRect: case LC32CoreGraphicsOpContextClipToRect: case LC32CoreGraphicsOpContextFillEllipseInRect: case LC32CoreGraphicsOpContextStrokeEllipseInRect: { if(!RequireCoreGraphicsSlots(call, 5)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; const CGRect rect = SlotRect(call, 1); switch(static_cast<LC32CoreGraphicsOpcode>(opcode)) { case LC32CoreGraphicsOpContextAddEllipseInRect: CGContextAddEllipseInRect(context, rect); break; case LC32CoreGraphicsOpContextClipToRect: CGContextClipToRect(context, rect); break; case LC32CoreGraphicsOpContextFillEllipseInRect: CGContextFillEllipseInRect(context, rect); SyncBitmapBacking(context, FindBitmapBacking(context)); break; case LC32CoreGraphicsOpContextStrokeEllipseInRect: CGContextStrokeEllipseInRect(context, rect); SyncBitmapBacking(context, FindBitmapBacking(context)); break; default: break; } return 1; } case LC32CoreGraphicsOpContextSetFillColorWithColor: case LC32CoreGraphicsOpContextSetStrokeColorWithColor: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGColorRef color = SlotHostObject<CGColorRef>(call, 1); if(!context || !color) return 0; if(static_cast<LC32CoreGraphicsOpcode>(opcode) == LC32CoreGraphicsOpContextSetFillColorWithColor) { CGContextSetFillColorWithColor(context, color); } else { CGContextSetStrokeColorWithColor(context, color); } return 1; } case LC32CoreGraphicsOpContextSetRGBFillColor: case LC32CoreGraphicsOpContextSetRGBStrokeColor: { if(!RequireCoreGraphicsSlots(call, 5)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; if(static_cast<LC32CoreGraphicsOpcode>(opcode) == LC32CoreGraphicsOpContextSetRGBFillColor) { CGContextSetRGBFillColor(context, SlotCGFloat(call, 1), SlotCGFloat(call, 2), SlotCGFloat(call, 3), SlotCGFloat(call, 4)); } else { CGContextSetRGBStrokeColor(context, SlotCGFloat(call, 1), SlotCGFloat(call, 2), SlotCGFloat(call, 3), SlotCGFloat(call, 4)); } return 1; } case LC32CoreGraphicsOpContextSetShadowWithColor: { if(!RequireCoreGraphicsSlots(call, 5)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGColorRef color = SlotHostObject<CGColorRef>(call, 4); if(!context) return 0; CGContextSetShadowWithColor(context, CGSizeMake(SlotCGFloat(call, 1), SlotCGFloat(call, 2)), SlotCGFloat(call, 3), color); return 1; } case LC32CoreGraphicsOpContextSetShadow: { if(!RequireCoreGraphicsSlots(call, 4)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextSetShadow(context, CGSizeMake(SlotCGFloat(call, 1), SlotCGFloat(call, 2)), SlotCGFloat(call, 3)); return 1; } case LC32CoreGraphicsOpContextSetFillColor: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 guestComponents = SlotU32(call, 1); if(!context || !guestComponents) return 0;
const auto getFillColorSpace = GetContextFillColorSpaceFunction(); if(!getFillColorSpace) { ReportMissingContextFillColorSpaceFunction(); return 0; }
CGColorSpaceRef space = getFillColorSpace(context); if(!space || CGColorSpaceGetModel(space) == kCGColorSpaceModelPattern) { return 0; } const size_t colorComponentCount = CGColorSpaceGetNumberOfComponents(space); if(!colorComponentCount || colorComponentCount >= kMaximumColorComponents) { return 0; }
std::vector<CGFloat> components; const CGFloat *componentPointer = nullptr; if(!ReadGuestCGFloatArray(guestComponents, colorComponentCount + 1, false, components, componentPointer)) { return 0; } CGContextSetFillColor(context, componentPointer); return 1; } case LC32CoreGraphicsOpContextSetBlendMode: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 mode = SlotU32(call, 1); if(!context || mode > kCGBlendModePlusLighter) return 0; CGContextSetBlendMode(context, static_cast<CGBlendMode>(mode)); return 1; } case LC32CoreGraphicsOpContextSetInterpolationQuality: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 quality = SlotU32(call, 1); if(!context || quality > kCGInterpolationMedium) return 0; CGContextSetInterpolationQuality(context, static_cast<CGInterpolationQuality>(quality)); return 1; } case LC32CoreGraphicsOpContextSetLineCap: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 cap = SlotU32(call, 1); if(!context || cap > kCGLineCapSquare) return 0; CGContextSetLineCap(context, static_cast<CGLineCap>(cap)); return 1; } case LC32CoreGraphicsOpContextSetLineJoin: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 join = SlotU32(call, 1); if(!context || join > kCGLineJoinBevel) return 0; CGContextSetLineJoin(context, static_cast<CGLineJoin>(join)); return 1; } case LC32CoreGraphicsOpContextSetLineWidth: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextSetLineWidth(context, SlotCGFloat(call, 1)); return 1; } case LC32CoreGraphicsOpContextSetAllowsAntialiasing: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 allowsAntialiasing = SlotU32(call, 1); if(!context || allowsAntialiasing > 1) return 0; CGContextSetAllowsAntialiasing(context, allowsAntialiasing != 0); return 1; } case LC32CoreGraphicsOpContextSetAllowsFontSubpixelPositioning: case LC32CoreGraphicsOpContextSetAllowsFontSmoothing: case LC32CoreGraphicsOpContextSetAllowsFontSubpixelQuantization: case LC32CoreGraphicsOpContextSetShouldSmoothFonts: case LC32CoreGraphicsOpContextSetShouldSubpixelPositionFonts: case LC32CoreGraphicsOpContextSetShouldSubpixelQuantizeFonts: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 enabled = SlotU32(call, 1); if(!context || enabled > 1) return 0; switch(opcode) { case LC32CoreGraphicsOpContextSetAllowsFontSubpixelPositioning: CGContextSetAllowsFontSubpixelPositioning(context, enabled != 0); break; case LC32CoreGraphicsOpContextSetAllowsFontSmoothing: CGContextSetAllowsFontSmoothing(context, enabled != 0); break; case LC32CoreGraphicsOpContextSetAllowsFontSubpixelQuantization: CGContextSetAllowsFontSubpixelQuantization(context, enabled != 0); break; case LC32CoreGraphicsOpContextSetShouldSmoothFonts: CGContextSetShouldSmoothFonts(context, enabled != 0); break; case LC32CoreGraphicsOpContextSetShouldSubpixelPositionFonts: CGContextSetShouldSubpixelPositionFonts(context, enabled != 0); break; default: CGContextSetShouldSubpixelQuantizeFonts(context, enabled != 0); break; } return 1; } case LC32CoreGraphicsOpContextSetShouldAntialias: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 shouldAntialias = SlotU32(call, 1); if(!context || shouldAntialias > 1) return 0; CGContextSetShouldAntialias(context, shouldAntialias != 0); return 1; } case LC32CoreGraphicsOpContextSetTextMatrix: { if(!RequireCoreGraphicsSlots(call, 7)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); if(!context) return 0; CGContextSetTextMatrix(context, CGAffineTransformMake( SlotCGFloat(call, 1), SlotCGFloat(call, 2), SlotCGFloat(call, 3), SlotCGFloat(call, 4), SlotCGFloat(call, 5), SlotCGFloat(call, 6))); return 1; } case LC32CoreGraphicsOpImageCreateWithImageInRect: { if(!RequireCoreGraphicsSlots(call, 5)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); if(!image) return 0; CGImageRef result = CGImageCreateWithImageInRect( image, SlotRect(call, 1)); return result ? LC32GuestObjectForOwnedHostObject(result) : 0; } case LC32CoreGraphicsOpImageCreateWithMask: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); CGImageRef mask = SlotHostObject<CGImageRef>(call, 1); if(!image || !mask) return 0; CGImageRef result = CGImageCreateWithMask(image, mask); return result ? LC32GuestObjectForOwnedHostObject(result) : 0; } case LC32CoreGraphicsOpImageCreateCopy: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); if(!image) return 0; CGImageRef result = CGImageCreateCopy(image); return result ? LC32GuestObjectForOwnedHostObject(result) : 0; } case LC32CoreGraphicsOpImageGetAlphaInfo: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); return image ? static_cast<u32>(CGImageGetAlphaInfo(image)) : 0; } case LC32CoreGraphicsOpImageGetBitmapInfo: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); return image ? static_cast<u32>(CGImageGetBitmapInfo(image)) : 0; } case LC32CoreGraphicsOpImageGetBitsPerComponent: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); return image ? static_cast<u32>(CGImageGetBitsPerComponent(image)) : 0; } case LC32CoreGraphicsOpImageGetColorSpace: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); CGColorSpaceRef space = image ? CGImageGetColorSpace(image) : nullptr; return space ? [(id)space guest_self] : 0; } case LC32CoreGraphicsOpImageGetDataProvider: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); CGDataProviderRef provider = image ? CGImageGetDataProvider(image) : nullptr; return provider ? [(id)provider guest_self] : 0; } case LC32CoreGraphicsOpPathAddArcToPoint: case LC32CoreGraphicsOpPathAddCurveToPoint: case LC32CoreGraphicsOpPathAddRect: case LC32CoreGraphicsOpPathAddEllipseInRect: case LC32CoreGraphicsOpPathAddQuadCurveToPoint: { const auto operation = static_cast<LC32CoreGraphicsOpcode>(opcode); const u32 expectedSlots = operation == LC32CoreGraphicsOpPathAddArcToPoint ? 13 : operation == LC32CoreGraphicsOpPathAddCurveToPoint ? 14 : 12; if(!RequireCoreGraphicsSlots(call, expectedSlots)) return 0; CGMutablePathRef path = SlotHostObject<CGMutablePathRef>(call, 0); if(!path) return 0; CGAffineTransform transformStorage; const CGAffineTransform *transform; if(!SlotOptionalTransform(call, 1, 2, transformStorage, transform)) return 0; if(operation == LC32CoreGraphicsOpPathAddArcToPoint) { CGPathAddArcToPoint(path, transform, SlotCGFloat(call, 8), SlotCGFloat(call, 9), SlotCGFloat(call, 10), SlotCGFloat(call, 11), SlotCGFloat(call, 12)); } else if(operation == LC32CoreGraphicsOpPathAddCurveToPoint) { CGPathAddCurveToPoint(path, transform, SlotCGFloat(call, 8), SlotCGFloat(call, 9), SlotCGFloat(call, 10), SlotCGFloat(call, 11), SlotCGFloat(call, 12), SlotCGFloat(call, 13)); } else if(operation == LC32CoreGraphicsOpPathAddQuadCurveToPoint) { CGPathAddQuadCurveToPoint(path, transform, SlotCGFloat(call, 8), SlotCGFloat(call, 9), SlotCGFloat(call, 10), SlotCGFloat(call, 11)); } else { if(operation == LC32CoreGraphicsOpPathAddEllipseInRect) CGPathAddEllipseInRect(path, transform, SlotRect(call, 8)); else CGPathAddRect(path, transform, SlotRect(call, 8)); } return 1; } case LC32CoreGraphicsOpContextSetStrokeColorSpace: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); CGColorSpaceRef space = SlotHostObject<CGColorSpaceRef>(call, 1); if(!context || !space) return 0; CGContextSetStrokeColorSpace(context, space); return 1; } case LC32CoreGraphicsOpContextSetStrokeColor: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); const u32 guestComponents = SlotU32(call, 1); if(!context || !guestComponents) return 0;
const auto getStrokeColorSpace = GetContextStrokeColorSpaceFunction(); if(!getStrokeColorSpace) { ReportMissingContextStrokeColorSpaceFunction(); return 0; }
CGColorSpaceRef space = getStrokeColorSpace(context); if(!space || CGColorSpaceGetModel(space) == kCGColorSpaceModelPattern) { return 0; } const size_t colorComponentCount = CGColorSpaceGetNumberOfComponents(space); if(!colorComponentCount || colorComponentCount >= kMaximumColorComponents) { return 0; }
std::vector<CGFloat> components; const CGFloat *componentPointer = nullptr; if(!ReadGuestCGFloatArray(guestComponents, colorComponentCount + 1, false, components, componentPointer)) { return 0; } CGContextSetStrokeColor(context, componentPointer); return 1; } case LC32CoreGraphicsOpContextStrokeLineSegments: { if(!RequireCoreGraphicsSlots(call, 3)) return 0; CGContextRef context = SlotHostObject<CGContextRef>(call, 0); std::vector<CGPoint> points; if(!context || !ReadGuestPoints(SlotU32(call, 1), SlotU32(call, 2), points)) { return 0; } CGContextStrokeLineSegments( context, points.data(), points.size()); SyncBitmapBacking(context, FindBitmapBacking(context)); return 1; } case LC32CoreGraphicsOpImageGetBitsPerPixel: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); return image ? static_cast<u32>(CGImageGetBitsPerPixel(image)) : 0; } case LC32CoreGraphicsOpImageGetBytesPerRow: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGImageRef image = SlotHostObject<CGImageRef>(call, 0); return image ? static_cast<u32>(CGImageGetBytesPerRow(image)) : 0; } case LC32CoreGraphicsOpPathCreateCopy: { if(!RequireCoreGraphicsSlots(call, 1)) return 0; CGPathRef path = SlotHostObject<CGPathRef>(call, 0); if(!path) return 0; CGPathRef result = CGPathCreateCopy(path); return result ? LC32GuestObjectForOwnedHostObject(result) : 0; } case LC32CoreGraphicsOpPathGetBoundingBox: { if(!RequireCoreGraphicsSlots(call, 2)) return 0; CGPathRef path = SlotHostObject<CGPathRef>(call, 0); return path && WriteGuestRect( SlotU32(call, 1), CGPathGetBoundingBox(path)); } } return 0;}
__END_DECLS