private static CGSize RCTTargetSize(CGSize sourceSize, nfloat sourceScale,
                                            CGSize destSize, nfloat destScale,
                                            RCTResizeMode resizeMode,
                                            bool allowUpscaling)
        {
            switch (resizeMode)
            {
            case RCTResizeMode.ScaleToFill:

                if (!allowUpscaling)
                {
                    nfloat scale = sourceScale / destScale;
                    destSize.Width  = (nfloat)Math.Min(sourceSize.Width * scale, destSize.Width);
                    destSize.Height = (nfloat)Math.Min(sourceSize.Height * scale, destSize.Height);
                }
                return(RCTCeilSize(destSize, destScale));

            default: {
                // Get target size
                CGSize size = RCTTargetRect(sourceSize, destSize, destScale, resizeMode).Size;
                if (!allowUpscaling)
                {
                    // return sourceSize if target size is larger
                    if (sourceSize.Width * sourceScale < size.Width * destScale)
                    {
                        return(sourceSize);
                    }
                }
                return(size);
            }
            }
        }
		private static CGSize RCTTargetSize(CGSize sourceSize, nfloat sourceScale,
			CGSize destSize, nfloat destScale,
			RCTResizeMode resizeMode,
			bool allowUpscaling)
		{
			switch (resizeMode)
			{
				case RCTResizeMode.ScaleToFill:

					if (!allowUpscaling)
					{
						nfloat scale = sourceScale / destScale;
						destSize.Width = (nfloat)Math.Min(sourceSize.Width * scale, destSize.Width);
						destSize.Height = (nfloat)Math.Min(sourceSize.Height * scale, destSize.Height);
					}
					return RCTCeilSize(destSize, destScale);

				default: {

						// Get target size
						CGSize size = RCTTargetRect(sourceSize, destSize, destScale, resizeMode).Size;
						if (!allowUpscaling)
						{
							// return sourceSize if target size is larger
							if (sourceSize.Width * sourceScale < size.Width * destScale)
							{
								return sourceSize;
							}
						}
						return size;
					}
			}
		}
        // Shamelessly copied from React-Native: https://github.com/facebook/react-native/blob/2cbc9127560c5f0f89ae5aa6ff863b1818f1c7c3/Libraries/Image/RCTImageUtils.m
        public static UIImage ToImage(this NSData data, CGSize destSize, nfloat destScale, RCTResizeMode resizeMode = RCTResizeMode.ScaleAspectFit, ImageInformation imageinformation = null, bool allowUpscale = false)
        {
            using (var sourceRef = CGImageSource.FromData(data))
            {
                var imageProperties = GetImageProperties(sourceRef);

                if (imageProperties == null)
                {
                    throw new BadImageFormatException("Image is null");
                }

                if (imageinformation != null)
                {
                    if (imageProperties.PixelWidth.HasValue && imageProperties.PixelHeight.HasValue)
                    {
                        imageinformation.SetOriginalSize(imageProperties.PixelWidth.Value, imageProperties.PixelHeight.Value);
                    }
                }

                var sourceSize = new CGSize((nfloat)imageProperties.PixelWidth, (nfloat)imageProperties.PixelHeight);

                if (destSize.IsEmpty)
                {
                    destSize = sourceSize;
                    if (destScale <= 0)
                    {
                        destScale = 1;
                    }
                }
                else if (destScale <= 0)
                {
                    destScale = ScaleHelper.Scale;
                }

                // Calculate target size
                CGSize targetSize      = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, allowUpscale);
                CGSize targetPixelSize = RCTSizeInPixels(targetSize, destScale);
                int    maxPixelSize    = (int)Math.Max(
                    Math.Min(sourceSize.Width, targetPixelSize.Width),
                    Math.Min(sourceSize.Height, targetPixelSize.Height)
                    );

                var options = new CGImageThumbnailOptions()
                {
                    ShouldAllowFloat               = true,
                    CreateThumbnailWithTransform   = true,
                    CreateThumbnailFromImageAlways = true,
                    MaxPixelSize = maxPixelSize,
                    ShouldCache  = false,
                };

                UIImage image = null;

                // gif
                if (sourceRef.ImageCount > 1)
                {
                    image = GifHelper.AnimateGif(sourceRef, destScale);
                }
                else
                {
                    // Get thumbnail
                    using (var imageRef = sourceRef.CreateThumbnail(0, options))
                    {
                        if (imageRef != null)
                        {
                            // Return image
                            image = new UIImage(imageRef, destScale, UIImageOrientation.Up);
                        }
                    }
                }

                if (imageinformation != null && image != null)
                {
                    int width  = (int)image.Size.Width;
                    int height = (int)image.Size.Height;
                    imageinformation.SetCurrentSize(width.PointsToPixels(), height.PointsToPixels());
                }

                return(image);
            }
        }
        private static CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, nfloat destScale, RCTResizeMode resizeMode)
        {
            if (destSize.IsEmpty)
            {
                // Assume we require the largest size available
                return(new CGRect(CGPoint.Empty, sourceSize));
            }

            nfloat aspect = sourceSize.Width / sourceSize.Height;

            // If only one dimension in destSize is non-zero (for example, an Image
            // with `flex: 1` whose height is indeterminate), calculate the unknown
            // dimension based on the aspect ratio of sourceSize
            if (destSize.Width == 0)
            {
                destSize.Width = destSize.Height * aspect;
            }
            if (destSize.Height == 0)
            {
                destSize.Height = destSize.Width / aspect;
            }

            // Calculate target aspect ratio if needed (don't bother if resizeMode == scale to fill)
            nfloat targetAspect = 0.0f;

            if (resizeMode != RCTResizeMode.ScaleToFill)
            {
                targetAspect = destSize.Width / destSize.Height;
                if (aspect == targetAspect)
                {
                    resizeMode = RCTResizeMode.ScaleToFill;
                }
            }

            switch (resizeMode)
            {
            case RCTResizeMode.ScaleToFill:
                return(new CGRect(CGPoint.Empty, RCTCeilSize(destSize, destScale)));

            case RCTResizeMode.ScaleAspectFit:
                if (targetAspect <= aspect)                         // target is taller than content
                {
                    sourceSize.Width  = destSize.Width = destSize.Width;
                    sourceSize.Height = sourceSize.Width / aspect;
                }
                else                         // target is wider than content
                {
                    sourceSize.Height = destSize.Height = destSize.Height;
                    sourceSize.Width  = sourceSize.Height * aspect;
                }

                return(new CGRect(
                           new CGPoint(
                               RCTFloorValue((destSize.Width - sourceSize.Width) / 2, destScale),
                               RCTFloorValue((destSize.Height - sourceSize.Height) / 2, destScale)
                               ),
                           RCTCeilSize(sourceSize, destScale)
                           ));

            case RCTResizeMode.ScaleAspectFill:
            default:
                if (targetAspect <= aspect)                           // target is taller than content

                {
                    sourceSize.Height = destSize.Height = destSize.Height;
                    sourceSize.Width  = sourceSize.Height * aspect;
                    destSize.Width    = destSize.Height * targetAspect;
                    return(new CGRect(
                               new CGPoint(
                                   RCTFloorValue((destSize.Width - sourceSize.Width) / 2, destScale),
                                   0
                                   ),
                               RCTCeilSize(sourceSize, destScale)
                               ));
                }
                else                         // target is wider than content
                {
                    sourceSize.Width  = destSize.Width = destSize.Width;
                    sourceSize.Height = sourceSize.Width / aspect;
                    destSize.Height   = destSize.Width / targetAspect;
                    return(new CGRect(
                               new CGPoint(
                                   0,
                                   RCTFloorValue((destSize.Height - sourceSize.Height) / 2, destScale)
                                   ),
                               RCTCeilSize(sourceSize, destScale)
                               ));
                }
            }
        }
Example #5
0
        public static async Task <IDecodedImage <PImage> > SourceRegfToDecodedImageAsync(NSData nsdata, CGSize destSize, nfloat destScale, Configuration config, TaskParameter parameters, RCTResizeMode resizeMode = RCTResizeMode.ScaleAspectFit, ImageInformation imageinformation = null, bool allowUpscale = false)
        {
            using (var sourceRef = CGImageSource.FromData(nsdata))
            {
                if (sourceRef == null)
                {
                    throw new BadImageFormatException("Decoded image is null or corrupted");
                }

                var imageProperties = sourceRef.GetProperties(0);

                if (imageProperties == null || !imageProperties.PixelWidth.HasValue || !imageProperties.PixelHeight.HasValue)
                {
                    throw new BadImageFormatException("Can't read image size properties. File corrupted?");
                }

                imageinformation.SetOriginalSize(imageProperties.PixelWidth.Value, imageProperties.PixelHeight.Value);

                var sourceSize = new CGSize(imageProperties.PixelWidth.Value, imageProperties.PixelHeight.Value);

                if (destSize.IsEmpty)
                {
                    destSize = sourceSize;
                    if (destScale <= 0)
                    {
                        destScale = 1;
                    }
                }
                else if (destScale <= 0)
                {
                    destScale = ScaleHelper.Scale;
                }

                // Calculate target size
                var targetSize      = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, allowUpscale);
                var targetPixelSize = RCTSizeInPixels(targetSize, destScale);
                var maxPixelSize    = (int)Math.Max(
                    allowUpscale ? targetPixelSize.Width : Math.Min(sourceSize.Width, targetPixelSize.Width),
                    allowUpscale ? targetPixelSize.Height : Math.Min(sourceSize.Height, targetPixelSize.Height)
                    );

                var options = new CGImageThumbnailOptions()
                {
                    ShouldAllowFloat               = true,
                    CreateThumbnailWithTransform   = true,
                    CreateThumbnailFromImageAlways = true,
                    MaxPixelSize = maxPixelSize,
                    ShouldCache  = false,
                };

                PImage image = null;
                IAnimatedImage <PImage>[] images = null;

                // GIF
                if (sourceRef.ImageCount > 1 && config.AnimateGifs && imageinformation.Type != ImageInformation.ImageType.ICO)
                {
                    try
                    {
                        await _gifLock.WaitAsync().ConfigureAwait(false);

                        var frameCount = sourceRef.ImageCount;

                        // no need to animate, fail safe.
                        if (frameCount <= 1)
                        {
                            using (var imageRef = sourceRef.CreateThumbnail(0, options))
                            {
#if __IOS__ || __TVOS__
                                image = new PImage(imageRef, destScale, UIImageOrientation.Up);
#elif __MACOS__
                                image = new PImage(imageRef, CGSize.Empty);
#endif
                            }
                        }

#if __IOS__ || __TVOS__
                        var frames         = GetFrames(sourceRef, options);
                        var delays         = GetDelays(sourceRef);
                        var totalDuration  = delays.Sum();
                        var adjustedFrames = AdjustFramesToSpoofDurations(frames, destScale, delays, totalDuration);
                        var avgDuration    = (double)totalDuration / adjustedFrames.Length;

                        images = new AnimatedImage <PImage> [adjustedFrames.Length];

                        for (int i = 0; i < images.Length; i++)
                        {
                            images[i] = new AnimatedImage <PImage>()
                            {
                                Image = adjustedFrames[i], Delay = (int)avgDuration
                            };
                        }
#elif __MACOS__
                        images = new AnimatedImage <PImage> [frameCount];
                        var delays = GetDelays(sourceRef);

                        for (var i = 0; i < images.Length; i++)
                        {
                            var nsImage = new PImage(sourceRef.CreateThumbnail(i, options), CGSize.Empty);
                            images[i] = new AnimatedImage <PImage>()
                            {
                                Image = nsImage, Delay = delays[i]
                            };
                        }
#endif
                    }
                    finally
                    {
                        _gifLock.Release();
                    }
                }
                else
                {
                    // Get thumbnail
                    using (var imageRef = sourceRef.CreateThumbnail(0, options))
                    {
                        if (imageRef != null)
                        {
#if __IOS__ || __TVOS__
                            image = new PImage(imageRef, destScale, UIImageOrientation.Up);
#elif __MACOS__
                            image = new PImage(imageRef, CGSize.Empty);
#endif
                        }
                    }
                }

                var result = new DecodedImage <PImage>();

                if (images != null && images.Length > 1)
                {
                    result.IsAnimated     = true;
                    result.AnimatedImages = images;

                    if (imageinformation != null)
                    {
                        var width  = (int)images[0].Image.Size.Width;
                        var height = (int)images[0].Image.Size.Height;
                        imageinformation.SetCurrentSize(width.DpToPixels(), height.DpToPixels());
                    }
                }
                else
                {
                    result.Image = image;

                    if (imageinformation != null)
                    {
                        var width  = (int)image.Size.Width;
                        var height = (int)image.Size.Height;
                        imageinformation.SetCurrentSize(width.DpToPixels(), height.DpToPixels());
                    }
                }

                return(result);
            }
        }
        // Shamelessly copied from React-Native: https://github.com/facebook/react-native/blob/2cbc9127560c5f0f89ae5aa6ff863b1818f1c7c3/Libraries/Image/RCTImageUtils.m
        public static UIImage ToImage(this NSData data, CGSize destSize, nfloat destScale, RCTResizeMode resizeMode = RCTResizeMode.ScaleAspectFit, ImageInformation imageinformation = null)
        {
            using (var sourceRef = CGImageSource.FromData(data))
            {
                if (sourceRef == null)
                {
                    return null;
                }

                // Get original image size
                var imageProperties = sourceRef.GetProperties(0);

                if (imageProperties == null)
                {
                    return null;
                }

                if (imageinformation != null)
                {
                    if (imageProperties.PixelWidth.HasValue && imageProperties.PixelHeight.HasValue)
                        imageinformation.SetOriginalSize(imageProperties.PixelWidth.Value, imageProperties.PixelHeight.Value);
                }

                var sourceSize = new CGSize((nfloat)imageProperties.PixelWidth, (nfloat)imageProperties.PixelHeight);

                if (destSize.IsEmpty)
                {
                    destSize = sourceSize;
                    if (destScale <= 0)
                    {
                        destScale = 1;
                    }
                }
                else if (destScale <= 0)
                {
                    destScale = ScaleHelper.Scale;
                }

                // Calculate target size
                CGSize targetSize = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, false);
                CGSize targetPixelSize = RCTSizeInPixels(targetSize, destScale);
                int maxPixelSize = (int)Math.Max(
                    Math.Min(sourceSize.Width, targetPixelSize.Width),
                    Math.Min(sourceSize.Height, targetPixelSize.Height)
                );

                var options = new CGImageThumbnailOptions()
                {
                        ShouldAllowFloat = true,
                        CreateThumbnailWithTransform = true,
                        CreateThumbnailFromImageAlways = true,
                        MaxPixelSize = maxPixelSize,
                        ShouldCache = false,
                };

                // Get thumbnail
                using (var imageRef = sourceRef.CreateThumbnail(0, options))
                {
                    if (imageRef == null)
                    {
                        return null;
                    }

                    // Return image
                    var image = new UIImage(imageRef, destScale, UIImageOrientation.Up);

                    if (imageinformation != null)
                    {
                        int width = (int)image.Size.Width;
                        int height = (int)image.Size.Height;
                        imageinformation.SetCurrentSize(width.PointsToPixels(), height.PointsToPixels());
                    }

                    return image;
                }
            }
        }
        private static CGRect RCTTargetRect(CGSize sourceSize, CGSize destSize, nfloat destScale, RCTResizeMode resizeMode)
        {
            if (destSize.IsEmpty)
            {
                // Assume we require the largest size available
                return new CGRect(CGPoint.Empty, sourceSize);
            }

            nfloat aspect = sourceSize.Width / sourceSize.Height;
            // If only one dimension in destSize is non-zero (for example, an Image
            // with `flex: 1` whose height is indeterminate), calculate the unknown
            // dimension based on the aspect ratio of sourceSize
            if (destSize.Width == 0)
            {
                destSize.Width = destSize.Height * aspect;
            }
            if (destSize.Height == 0)
            {
                destSize.Height = destSize.Width / aspect;
            }

            // Calculate target aspect ratio if needed (don't bother if resizeMode == scale to fill)
            nfloat targetAspect = 0.0f;
            if (resizeMode != RCTResizeMode.ScaleToFill)
            {
                targetAspect = destSize.Width / destSize.Height;
                if (aspect == targetAspect)
                {
                    resizeMode = RCTResizeMode.ScaleToFill;
                }
            }

            switch (resizeMode)
            {
                case RCTResizeMode.ScaleToFill:
                    return new CGRect(CGPoint.Empty, RCTCeilSize(destSize, destScale));

                case RCTResizeMode.ScaleAspectFit:
                    if (targetAspect <= aspect) // target is taller than content
                    {
                        sourceSize.Width = destSize.Width = destSize.Width;
                        sourceSize.Height = sourceSize.Width / aspect;

                    }
                    else // target is wider than content
                    {
                        sourceSize.Height = destSize.Height = destSize.Height;
                        sourceSize.Width = sourceSize.Height * aspect;
                    }

                    return new CGRect(
                        new CGPoint(
                            RCTFloorValue((destSize.Width - sourceSize.Width) / 2, destScale),
                            RCTFloorValue((destSize.Height - sourceSize.Height) / 2, destScale)
                        ),
                        RCTCeilSize(sourceSize, destScale)
                    );

                case RCTResizeMode.ScaleAspectFill:
                default:
                    if (targetAspect <= aspect) { // target is taller than content

                        sourceSize.Height = destSize.Height = destSize.Height;
                        sourceSize.Width = sourceSize.Height * aspect;
                        destSize.Width = destSize.Height * targetAspect;
                        return new CGRect(
                            new CGPoint(
                                RCTFloorValue((destSize.Width - sourceSize.Width) / 2, destScale),
                                0
                            ),
                            RCTCeilSize(sourceSize, destScale)
                        );
                    }
                    else // target is wider than content
                    {
                        sourceSize.Width = destSize.Width = destSize.Width;
                        sourceSize.Height = sourceSize.Width / aspect;
                        destSize.Height = destSize.Width / targetAspect;
                        return new CGRect(
                            new CGPoint(
                                0,
                                RCTFloorValue((destSize.Height - sourceSize.Height) / 2, destScale)
                            ),
                            RCTCeilSize(sourceSize, destScale)
                        );
                    }
            }
        }
Example #8
0
        // Shamelessly copied from React-Native: https://github.com/facebook/react-native/blob/2cbc9127560c5f0f89ae5aa6ff863b1818f1c7c3/Libraries/Image/RCTImageUtils.m
        public static NSImage ToImage(this NSData data, CGSize destSize, nfloat destScale, Configuration config, TaskParameter parameters, RCTResizeMode resizeMode = RCTResizeMode.ScaleAspectFit, ImageInformation imageinformation = null, bool allowUpscale = false)
        {
            using (var sourceRef = CGImageSource.FromData(data))
            {
                if (sourceRef == null)
                {
                    throw new BadImageFormatException("Decoded image is null or corrupted");
                }

                var imageProperties = sourceRef.GetProperties(0);

                if (imageProperties == null || !imageProperties.PixelWidth.HasValue || !imageProperties.PixelHeight.HasValue)
                {
                    throw new BadImageFormatException("Can't read image size properties. File corrupted?");
                }

                imageinformation.SetOriginalSize(imageProperties.PixelWidth.Value, imageProperties.PixelHeight.Value);

                var sourceSize = new CGSize(imageProperties.PixelWidth.Value, imageProperties.PixelHeight.Value);
                if (destSize.IsEmpty)
                {
                    destSize = sourceSize;
                    if (destScale <= 0)
                    {
                        destScale = 1;
                    }
                }
                else if (destScale <= 0)
                {
                    destScale = ScaleHelper.Scale;
                }

                // Calculate target size
                CGSize targetSize      = RCTTargetSize(sourceSize, 1, destSize, destScale, resizeMode, allowUpscale);
                CGSize targetPixelSize = RCTSizeInPixels(targetSize, destScale);
                int    maxPixelSize    = (int)Math.Max(
                    allowUpscale ? targetPixelSize.Width : Math.Min(sourceSize.Width, targetPixelSize.Width),
                    allowUpscale ? targetPixelSize.Height : Math.Min(sourceSize.Height, targetPixelSize.Height)
                    );

                var options = new CGImageThumbnailOptions()
                {
                    ShouldAllowFloat               = true,
                    CreateThumbnailWithTransform   = true,
                    CreateThumbnailFromImageAlways = true,
                    MaxPixelSize = maxPixelSize,
                    ShouldCache  = false,
                };

                NSImage image = null;

                // Get thumbnail
                using (var imageRef = sourceRef.CreateThumbnail(0, options))
                {
                    if (imageRef != null)
                    {
                        // Return image
                        image = new NSImage(imageRef, CGSize.Empty);
                    }
                }


                if (imageinformation != null && image != null)
                {
                    int width  = (int)image.Size.Width;
                    int height = (int)image.Size.Height;
                    imageinformation.SetCurrentSize(width.PointsToPixels(), height.PointsToPixels());
                }

                return(image);
            }
        }