[ImageIO] Update bindings for Xcode11 (#8376)
* initial imagio bindings + test project * remove sample files, update xtro * clean up * add new line at eof * even more cleanup... * update based on PR feedback * reformat according to coding standards * fix all feedback except monotouch tests * add monotouch-test files * fix test feedback * fix PR feedback * fix timeout in tests, add asserts for status, update return value for APi to CGImageAnimationStatus enum * fix == asserts * respond to more pr feedback * add StrongDictionary, remove Partial * NSNumber -> nuint for NSUInteger * add smaller gif * remove hack.gif from project * add gif to csproj
This commit is contained in:
Родитель
95d71c8ee1
Коммит
ecf295f4db
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// ImageIO - CGImageAnimation.cs
|
||||
//
|
||||
// Authors:
|
||||
// Whitney Schmidt <whschm@microsoft.com>
|
||||
//
|
||||
// Copyright 2020, Microsoft Corp.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using CoreFoundation;
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using ObjCRuntime;
|
||||
|
||||
namespace ImageIO
|
||||
{
|
||||
|
||||
public static class CGImageAnimation
|
||||
{
|
||||
|
||||
public delegate void CGImageSourceAnimationHandler (nint index, CGImage image, out bool stop);
|
||||
|
||||
[Introduced (PlatformName.MacOSX, 10, 15, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.iOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.TvOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.WatchOS, 6, 0, PlatformArchitecture.All)]
|
||||
[DllImport (Constants.ImageIOLibrary)]
|
||||
static extern /* OSStatus */ CGImageAnimationStatus CGAnimateImageAtURLWithBlock ( /* CFURLRef */ IntPtr url, /* CFDictionaryRef _iio_Nullable */ IntPtr options, /* CGImageSourceAnimationHandler */ ref BlockLiteral block);
|
||||
|
||||
[Introduced (PlatformName.MacOSX, 10, 15, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.iOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.TvOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.WatchOS, 6, 0, PlatformArchitecture.All)]
|
||||
[DllImport (Constants.ImageIOLibrary)]
|
||||
static extern /* OSStatus */ CGImageAnimationStatus CGAnimateImageDataWithBlock ( /* CFDataRef _Nonnull */ IntPtr data, /* CFDictionaryRef _Nullable */ IntPtr options, /* CGImageSourceAnimationHandler _Nonnull */ ref BlockLiteral block);
|
||||
|
||||
[Introduced (PlatformName.MacOSX, 10, 15, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.iOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.TvOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.WatchOS, 6, 0, PlatformArchitecture.All)]
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public static CGImageAnimationStatus AnimateImage (NSUrl url, CGImageAnimationOptions options, [BlockProxy (typeof (NIDCGImageSourceAnimationBlock))] CGImageSourceAnimationHandler handler)
|
||||
{
|
||||
#if IOS && ARCH_32
|
||||
throw new PlatformNotSupportedException ("This API is not supported on this version of iOS");
|
||||
#else
|
||||
if (url == null)
|
||||
throw new ArgumentNullException (nameof (url));
|
||||
if (handler == null)
|
||||
throw new ArgumentNullException (nameof (handler));
|
||||
|
||||
var block = new BlockLiteral ();
|
||||
block.SetupBlockUnsafe (SDCGImageSourceAnimationBlock.Handler, handler);
|
||||
|
||||
try {
|
||||
return CGAnimateImageAtURLWithBlock (url.Handle, options.GetHandle (), ref block);
|
||||
} finally {
|
||||
block.CleanupBlock ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
[Introduced (PlatformName.MacOSX, 10, 15, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.iOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.TvOS, 13, 0, PlatformArchitecture.All)]
|
||||
[Introduced (PlatformName.WatchOS, 6, 0, PlatformArchitecture.All)]
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public static CGImageAnimationStatus AnimateImage (NSData data, CGImageAnimationOptions options, [BlockProxy (typeof (NIDCGImageSourceAnimationBlock))] CGImageSourceAnimationHandler handler)
|
||||
{
|
||||
#if IOS && ARCH_32
|
||||
throw new PlatformNotSupportedException ("This API is not supported on this version of iOS");
|
||||
#else
|
||||
if (data == null)
|
||||
throw new ArgumentNullException (nameof (data));
|
||||
if (handler == null)
|
||||
throw new ArgumentNullException (nameof (handler));
|
||||
|
||||
var block = new BlockLiteral ();
|
||||
block.SetupBlockUnsafe (SDCGImageSourceAnimationBlock.Handler, handler);
|
||||
|
||||
try {
|
||||
return CGAnimateImageDataWithBlock (data.Handle, options.GetHandle (), ref block);
|
||||
} finally {
|
||||
block.CleanupBlock ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// This class bridges native block invocations that call into C#
|
||||
//
|
||||
static internal class SDCGImageSourceAnimationBlock
|
||||
{
|
||||
static internal readonly DCGImageSourceAnimationBlock Handler = Invoke;
|
||||
|
||||
[MonoPInvokeCallback (typeof (DCGImageSourceAnimationBlock))]
|
||||
static void Invoke (IntPtr block, nint index, IntPtr image, [MarshalAs (UnmanagedType.I1)] out bool stop)
|
||||
{
|
||||
var del = BlockLiteral.GetTarget<CGImageSourceAnimationHandler> (block);
|
||||
if (del != null)
|
||||
del (index, new CoreGraphics.CGImage (image), out stop);
|
||||
else
|
||||
stop = false;
|
||||
}
|
||||
} /* class SDCGImageSourceAnimationBlock */
|
||||
|
||||
internal sealed class NIDCGImageSourceAnimationBlock : TrampolineBlockBase
|
||||
{
|
||||
DCGImageSourceAnimationBlock invoker;
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public unsafe NIDCGImageSourceAnimationBlock (BlockLiteral * block) : base (block)
|
||||
{
|
||||
invoker = block->GetDelegateForBlock<DCGImageSourceAnimationBlock> ();
|
||||
}
|
||||
|
||||
[Preserve (Conditional = true)]
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
public unsafe static CGImageSourceAnimationHandler Create (IntPtr block)
|
||||
{
|
||||
if (block == IntPtr.Zero)
|
||||
return null;
|
||||
var del = (CGImageSourceAnimationHandler) GetExistingManagedDelegate (block);
|
||||
return del ?? new NIDCGImageSourceAnimationBlock ( (BlockLiteral *) block).Invoke;
|
||||
}
|
||||
|
||||
[BindingImpl (BindingImplOptions.Optimizable)]
|
||||
void Invoke (nint index, CGImage image, out bool stop)
|
||||
{
|
||||
invoker (BlockPointer, index, image.GetHandle (), out stop);
|
||||
}
|
||||
} /* class NIDCGImageSourceAnimationBlock */
|
||||
|
||||
[UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)]
|
||||
[UserDelegateType (typeof (CGImageSourceAnimationHandler))]
|
||||
internal delegate void DCGImageSourceAnimationBlock (IntPtr block, nint index, IntPtr imageHandle, [MarshalAs (UnmanagedType.I1)] out bool stop);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
// Sebastien Pouliot <sebastien@xamarin.com>
|
||||
//
|
||||
// Copyright 2013-2016, Xamarin Inc.
|
||||
// Copyright 2020, Microsoft Corp.
|
||||
//
|
||||
|
||||
using System;
|
||||
|
@ -60,4 +61,15 @@ namespace ImageIO {
|
|||
Average = 0x40,
|
||||
Paeth = 0x80
|
||||
}
|
||||
|
||||
[Mac (10, 15), iOS (13, 0), TV (13, 0), Watch (6, 0)]
|
||||
public enum CGImageAnimationStatus
|
||||
{
|
||||
Ok = 0,
|
||||
ParameterError = -22140,
|
||||
CorruptInputImage = -22141,
|
||||
UnsupportedFormat = -22142,
|
||||
IncompleteInputImage = -22143,
|
||||
AllocationFailure = -22144,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -927,6 +927,7 @@ IMAGEIO_CORE_SOURCES = \
|
|||
ImageIO/CGImageSource.cs \
|
||||
|
||||
IMAGEIO_SOURCES = \
|
||||
ImageIO/CGImageAnimation.cs \
|
||||
ImageIO/CGImageDestination.cs \
|
||||
ImageIO/CGImageMetadata.cs \
|
||||
ImageIO/CGImageMetadataTag.cs \
|
||||
|
|
|
@ -250,6 +250,18 @@ namespace ImageIO {
|
|||
[Field ("kCGImagePropertyExifGamma")]
|
||||
NSString ExifGamma { get; }
|
||||
|
||||
[Mac (10, 15, 1), iOS (13, 1), TV (13, 1), Watch (6, 1)]
|
||||
[Field ("kCGImagePropertyExifCompositeImage")]
|
||||
NSString ExifCompositeImage { get; }
|
||||
|
||||
[Mac (10, 15, 1), iOS (13, 1), TV (13, 1), Watch (6, 1)]
|
||||
[Field ("kCGImagePropertyExifSourceImageNumberOfCompositeImage")]
|
||||
NSString ExifSourceImageNumberOfCompositeImage { get; }
|
||||
|
||||
[Mac (10, 15, 1), iOS (13, 1), TV (13, 1), Watch (6, 1)]
|
||||
[Field ("kCGImagePropertyExifSourceExposureTimesOfCompositeImage")]
|
||||
NSString ExifSourceExposureTimesOfCompositeImage { get; }
|
||||
|
||||
// misdocumented (first 4.3, then 5.0) but the constants were not present until 6.x
|
||||
|
||||
[Field ("kCGImagePropertyExifCameraOwnerName")]
|
||||
|
@ -2356,4 +2368,28 @@ namespace ImageIO {
|
|||
NSData Data { get; set; }
|
||||
NSDictionary DataDescription { get; set; }
|
||||
}
|
||||
|
||||
[Mac (10, 15), iOS (13, 0), TV (13, 0), Watch (6, 0)]
|
||||
[Static]
|
||||
[Internal]
|
||||
interface CGImageAnimationOptionsKeys {
|
||||
[Field ("kCGImageAnimationDelayTime")]
|
||||
NSString DelayTimeKey { get; }
|
||||
|
||||
[Field ("kCGImageAnimationLoopCount")]
|
||||
NSString LoopCountKey { get; }
|
||||
|
||||
[Field ("kCGImageAnimationStartIndex")]
|
||||
NSString StartIndexKey { get; }
|
||||
}
|
||||
|
||||
[Mac (10, 15), iOS (13, 0), TV (13, 0), Watch (6, 0)]
|
||||
[StrongDictionary ("CGImageAnimationOptionsKeys")]
|
||||
interface CGImageAnimationOptions {
|
||||
double DelayTime { get; set; }
|
||||
|
||||
nuint LoopCount { get; set; }
|
||||
|
||||
nuint StartIndex { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
//
|
||||
// Unit tests for CGImageAnimation
|
||||
//
|
||||
// Authors:
|
||||
// Whitney Schmidt <whschm@microsoft.com>
|
||||
//
|
||||
// Copyright 2020 Microsoft Corp. All rights reserved.
|
||||
//
|
||||
using System;
|
||||
using System.IO;
|
||||
using Foundation;
|
||||
using System.Threading.Tasks;
|
||||
#if MONOMAC
|
||||
using AppKit;
|
||||
#else
|
||||
using UIKit;
|
||||
#endif
|
||||
using CoreGraphics;
|
||||
using ImageIO;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MonoTouchFixtures.ImageIO {
|
||||
|
||||
[TestFixture]
|
||||
[Preserve (AllMembers = true)]
|
||||
public class CGImageAnimationTest {
|
||||
|
||||
NSUrl imageUrl;
|
||||
NSData imageData;
|
||||
TaskCompletionSource<bool> tcs;
|
||||
int testValue;
|
||||
CGImageAnimationStatus status;
|
||||
|
||||
void MyHandlerSetValueZero (nint index, CGImage image, out bool stop)
|
||||
{
|
||||
stop = true;
|
||||
testValue = 0;
|
||||
tcs.TrySetResult (true);
|
||||
}
|
||||
|
||||
void MyHandlerSetValueOne (nint index, CGImage image, out bool stop)
|
||||
{
|
||||
stop = true;
|
||||
testValue = 1;
|
||||
tcs.TrySetResult (true);
|
||||
}
|
||||
|
||||
[TestFixtureSetUp]
|
||||
public void Init ()
|
||||
{
|
||||
TestRuntime.AssertXcodeVersion (11, 4);
|
||||
|
||||
imageUrl = NSBundle.MainBundle.GetUrlForResource ("square", "gif");
|
||||
imageData = NSData.FromUrl (imageUrl);
|
||||
}
|
||||
|
||||
[TestFixtureTearDown]
|
||||
public void Cleanup ()
|
||||
{
|
||||
imageUrl?.Dispose ();
|
||||
imageData?.Dispose ();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void InitPerTest ()
|
||||
{
|
||||
testValue = -1;
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void Dispose ()
|
||||
{
|
||||
testValue = -1;
|
||||
}
|
||||
|
||||
void CallAnimateImage (bool useUrl, CGImageAnimation.CGImageSourceAnimationHandler handler)
|
||||
{
|
||||
tcs = new TaskCompletionSource<bool> ();
|
||||
status = (CGImageAnimationStatus) 1; /* CGImageAnimationStatus.Ok == 0 */
|
||||
bool done = false;
|
||||
|
||||
TestRuntime.RunAsync (TimeSpan.FromSeconds (30), async () => {
|
||||
if (useUrl) {
|
||||
status = CGImageAnimation.AnimateImage (imageUrl, null, handler);
|
||||
} else {
|
||||
status = CGImageAnimation.AnimateImage (imageData, null, handler);
|
||||
}
|
||||
await tcs.Task;
|
||||
done = true;
|
||||
},
|
||||
() => done);
|
||||
|
||||
tcs = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithUrl ()
|
||||
{
|
||||
CallAnimateImage ( /* useUrl */ true, MyHandlerSetValueZero);
|
||||
Assert.AreEqual (CGImageAnimationStatus.Ok, status, "status ok: handler called with url");
|
||||
Assert.AreEqual (0, testValue, "handler called with url");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithData ()
|
||||
{
|
||||
CallAnimateImage ( /* useUrl */ false, MyHandlerSetValueZero);
|
||||
Assert.AreEqual (CGImageAnimationStatus.Ok, status, "status ok: handler called with data");
|
||||
Assert.AreEqual (0, testValue, "handler called with data");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithUrlChangeHandler ()
|
||||
{
|
||||
CallAnimateImage ( /* useUrl */ true, MyHandlerSetValueZero);
|
||||
Assert.AreEqual (CGImageAnimationStatus.Ok, status, "status ok: first handler called with url");
|
||||
Assert.AreEqual (0, testValue, "first handler called with url" );
|
||||
|
||||
CallAnimateImage ( /* useUrl */ true, MyHandlerSetValueOne);
|
||||
Assert.AreEqual (CGImageAnimationStatus.Ok, status, "status ok: second handler called with url");
|
||||
Assert.AreEqual (1, testValue, "second handler called with url");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithDataChangeHandler ()
|
||||
{
|
||||
CallAnimateImage ( /* useUrl */ false, MyHandlerSetValueZero);
|
||||
Assert.AreEqual (CGImageAnimationStatus.Ok, status, "status ok: first handler called with data");
|
||||
Assert.AreEqual (0, testValue, "first handler called with data");
|
||||
|
||||
CallAnimateImage ( /* useUrl */ false, MyHandlerSetValueOne);
|
||||
Assert.AreEqual (CGImageAnimationStatus.Ok, status, "status ok: second handler called with data");
|
||||
Assert.AreEqual (1, testValue, "second handler called with data");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithUrlNullUrl ()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException> (() => CGImageAnimation.AnimateImage ( (NSUrl) null, null, MyHandlerSetValueZero), "null url");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithUrlNullHandler ()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException> (() => CGImageAnimation.AnimateImage (imageUrl, null, /* CGImageSourceAnimationHandler */ null), "null handler called with url");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithDataNullData ()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException> (() => CGImageAnimation.AnimateImage ( (NSData) null, null, MyHandlerSetValueZero), "null data");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnimateImageWithDataNullHandler ()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException> (() => CGImageAnimation.AnimateImage (imageData, null, /* CGImageSourceAnimationHandler */ null), "null handler called with data");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 10 KiB |
|
@ -331,6 +331,7 @@
|
|||
<BundleResource Include="compressed_lzma" />
|
||||
<BundleResource Include="compressed_zip" />
|
||||
<BundleResource Include="example.pac" />
|
||||
<BundleResource Include="Resources\square.gif" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Metal Include="Resources\metal-sample.metal" Condition="'$(Platform)' != 'iPhoneSimulator' " />
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
!missing-enum! CGImageAnimationStatus not bound
|
||||
!missing-field! kCGImageAnimationDelayTime not bound
|
||||
!missing-field! kCGImageAnimationLoopCount not bound
|
||||
!missing-field! kCGImageAnimationStartIndex not bound
|
||||
!missing-pinvoke! CGAnimateImageAtURLWithBlock is not bound
|
||||
!missing-pinvoke! CGAnimateImageDataWithBlock is not bound
|
||||
## appended from unclassified file
|
||||
!missing-field! kCGImagePropertyExifCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceExposureTimesOfCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceImageNumberOfCompositeImage not bound
|
|
@ -1,9 +0,0 @@
|
|||
!missing-enum! CGImageAnimationStatus not bound
|
||||
!missing-field! kCGImageAnimationDelayTime not bound
|
||||
!missing-field! kCGImageAnimationLoopCount not bound
|
||||
!missing-field! kCGImageAnimationStartIndex not bound
|
||||
!missing-field! kCGImagePropertyExifCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceExposureTimesOfCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceImageNumberOfCompositeImage not bound
|
||||
!missing-pinvoke! CGAnimateImageAtURLWithBlock is not bound
|
||||
!missing-pinvoke! CGAnimateImageDataWithBlock is not bound
|
|
@ -1,10 +0,0 @@
|
|||
!missing-enum! CGImageAnimationStatus not bound
|
||||
!missing-field! kCGImageAnimationDelayTime not bound
|
||||
!missing-field! kCGImageAnimationLoopCount not bound
|
||||
!missing-field! kCGImageAnimationStartIndex not bound
|
||||
!missing-pinvoke! CGAnimateImageAtURLWithBlock is not bound
|
||||
!missing-pinvoke! CGAnimateImageDataWithBlock is not bound
|
||||
## appended from unclassified file
|
||||
!missing-field! kCGImagePropertyExifCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceExposureTimesOfCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceImageNumberOfCompositeImage not bound
|
|
@ -1,10 +0,0 @@
|
|||
!missing-enum! CGImageAnimationStatus not bound
|
||||
!missing-field! kCGImageAnimationDelayTime not bound
|
||||
!missing-field! kCGImageAnimationLoopCount not bound
|
||||
!missing-field! kCGImageAnimationStartIndex not bound
|
||||
!missing-pinvoke! CGAnimateImageAtURLWithBlock is not bound
|
||||
!missing-pinvoke! CGAnimateImageDataWithBlock is not bound
|
||||
## appended from unclassified file
|
||||
!missing-field! kCGImagePropertyExifCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceExposureTimesOfCompositeImage not bound
|
||||
!missing-field! kCGImagePropertyExifSourceImageNumberOfCompositeImage not bound
|
Загрузка…
Ссылка в новой задаче