Files
5.24BuYu/Assets/Plugins/ImageCropper/Scripts/ImageCropper.cs
T

1142 lines
34 KiB
C#

using UnityEngine;
using UnityEngine.UI;
using ImageCropperNamespace;
using System.Collections;
// ===
// Auto-zoom feature inspired from: Android Image Cropper https://github.com/ArthurHub/Android-Image-Cropper
// Copyright 2016, Arthur Teplitzki, 2013, Edmodo, Inc.
// License (Apache License 2.0) https://github.com/ArthurHub/Android-Image-Cropper/blob/master/LICENSE.txt
// ===
public class ImageCropper : MonoBehaviour
{
public class Settings
{
public bool autoZoomEnabled = true;
public bool pixelPerfectSelection = false;
public bool ovalSelection = false;
public bool markTextureNonReadable = true;
public Color imageBackground = Color.black;
public Button visibleButtons = ~Button.None;
public Visibility guidelinesVisibility = Visibility.AlwaysVisible;
public Orientation initialOrientation = Orientation.Normal;
public Vector2 selectionMinSize = Vector2.zero;
public Vector2 selectionMaxSize = Vector2.zero;
public float selectionMinAspectRatio = 0f;
public float selectionMaxAspectRatio = 0f;
public float selectionInitialPaddingLeft = 0.1f;
public float selectionInitialPaddingTop = 0.1f;
public float selectionInitialPaddingRight = 0.1f;
public float selectionInitialPaddingBottom = 0.1f;
}
[System.Flags]
public enum Direction { None = 0, Left = 1, Top = 2, Right = 4, Bottom = 8 };
[System.Flags]
public enum Button { None = 0, FlipHorizontal = 1, FlipVertical = 2, Rotate90Degrees = 4 };
public enum Visibility { Hidden = 0, OnDrag = 1, AlwaysVisible = 2 };
// EXIF orientation: http://sylvana.net/jpegcrop/exif_orientation.html (indices are reordered)
public enum Orientation { Normal = 0, Rotate90 = 1, Rotate180 = 2, Rotate270 = 3, FlipHorizontal = 4, Transpose = 5, FlipVertical = 6, Transverse = 7 };
public delegate void CropResult( bool result, Texture originalImage, Texture2D croppedImage );
public delegate void ImageResizePolicy( ref int width, ref int height );
private static ImageCropper m_instance = null;
public static ImageCropper Instance
{
get
{
if( m_instance == null )
m_instance = Instantiate( Resources.Load<ImageCropper>( "ImageCropper" ) );
return m_instance;
}
}
[Header( "Properties" )]
[SerializeField]
private float autoZoomInThreshold = 0.5f;
[SerializeField]
private float autoZoomOutThreshold = 0.65f;
[SerializeField]
private float autoZoomInFillAmount = 0.64f;
[SerializeField]
private float autoZoomOutFillAmount = 0.51f;
[SerializeField]
private AnimationCurve autoZoomCurve;
[SerializeField]
private float m_selectionSnapToEdgeThreshold = 5f;
public float SelectionSnapToEdgeThreshold { get { return m_selectionSnapToEdgeThreshold; } }
[SerializeField]
private float viewportScrollSpeed = 512f;
[Header( "Internal Variables" )]
[SerializeField]
private Canvas canvas;
[SerializeField]
private RectTransform m_viewport;
public RectTransform Viewport { get { return m_viewport; } }
[SerializeField]
private RectTransform m_imageHolder;
public RectTransform ImageHolder { get { return m_imageHolder; } }
[SerializeField]
private RawImage m_orientedImage;
public RawImage OrientedImage { get { return m_orientedImage; } }
[SerializeField]
private RectTransform m_selection;
public RectTransform Selection { get { return m_selection; } }
[SerializeField]
private RectTransform m_selectionGraphics;
public RectTransform SelectionGraphics { get { return m_selectionGraphics; } }
[SerializeField]
private SizeChangeListener viewportSizeChangeListener;
[SerializeField]
private SelectionGraphicsSynchronizer selectionGraphicsSynchronizer;
[SerializeField]
private Behaviour[] ovalMaskElements;
[SerializeField]
private Behaviour[] guidelines;
[Header( "User Interface" )]
[SerializeField]
private GameObject flipHorizontalButton;
[SerializeField]
private GameObject flipVerticalButton;
[SerializeField]
private GameObject rotate90DegreesButton;
[SerializeField]
private FontSizeSynchronizer textsSynchronizer;
[Header( "Crop Helpers" )]
[SerializeField]
private Canvas cropRenderCanvas;
[SerializeField]
private RawImage cropRenderImage;
[SerializeField]
private RectTransform cropRenderSelection;
[SerializeField]
private Camera cropRenderCamera;
public bool IsOpen { get { return gameObject.activeSelf; } }
private Settings m_defaultSettings;
private Settings DefaultSettings
{
get
{
if( m_defaultSettings == null )
m_defaultSettings = new Settings();
return m_defaultSettings;
}
}
private bool m_autoZoomEnabled;
public bool AutoZoomEnabled
{
get { return m_autoZoomEnabled; }
set
{
m_autoZoomEnabled = value;
if( m_autoZoomEnabled )
StartAutoZoom( false );
}
}
private bool m_pixelPerfectSelection;
public bool PixelPerfectSelection
{
get { return m_pixelPerfectSelection; }
set
{
m_pixelPerfectSelection = value;
if( m_pixelPerfectSelection )
MakePixelPerfectSelection();
}
}
private bool m_ovalSelection;
public bool OvalSelection
{
get { return m_ovalSelection; }
set
{
m_ovalSelection = value;
for( int i = 0; i < ovalMaskElements.Length; i++ )
ovalMaskElements[i].enabled = m_ovalSelection;
}
}
private Visibility m_guidelinesVisibility;
public Visibility GuidelinesVisibility
{
get { return m_guidelinesVisibility; }
set
{
m_guidelinesVisibility = value;
bool visible = m_guidelinesVisibility == Visibility.AlwaysVisible;
for( int i = 0; i < guidelines.Length; i++ )
guidelines[i].enabled = visible;
}
}
public bool MarkTextureNonReadable { get; set; }
public Color ImageBackground { get; set; }
private Vector2 m_viewportSize;
public Vector2 ViewportSize { get { return m_viewportSize; } }
private Vector2 m_originalImageSize;
public Vector2 OriginalImageSize { get { return m_originalImageSize; } }
private Vector2 m_orientedImageSize;
public Vector2 OrientedImageSize { get { return m_orientedImageSize; } }
public Vector2 SelectionSize { get { return m_selection.sizeDelta; } }
private RectTransform orientedImageTransform;
private IEnumerator autoZoomCoroutine;
private ISelectionHandler currentSelectionHandler;
private Orientation currentOrientation;
private CropResult cropCallback;
private ImageResizePolicy imageResizePolicy;
private bool shouldRefreshViewport;
private Vector2 minSize, maxSize;
private Vector2 currMinSize, currMaxSize;
private float minAspectRatio, maxAspectRatio;
private float minImageScale;
private void Awake()
{
if( m_instance == null )
m_instance = this;
else if( this != m_instance )
{
Destroy( gameObject );
return;
}
orientedImageTransform = (RectTransform) m_orientedImage.transform;
viewportSizeChangeListener.onSizeChanged = OnViewportDimensionsChange;
cropRenderCanvas.gameObject.layer = 22; // some random layer that is hopefully not used by any other object
cropRenderImage.gameObject.layer = 22;
cropRenderCamera.cullingMask = 1 << 22;
cropRenderCanvas.gameObject.SetActive( false );
gameObject.SetActive( false );
}
private void OnDisable()
{
autoZoomCoroutine = null;
}
private void LateUpdate()
{
if( gameObject.activeInHierarchy )
{
if( currentSelectionHandler != null && m_imageHolder.localScale.z > minImageScale + 0.01f )
currentSelectionHandler.OnUpdate();
if( shouldRefreshViewport )
{
textsSynchronizer.Synchronize();
ResetView( true );
shouldRefreshViewport = false;
}
selectionGraphicsSynchronizer.Synchronize();
}
}
private void OnViewportDimensionsChange( Vector2 size )
{
m_viewportSize = size;
shouldRefreshViewport = true;
}
public void Show( Texture image, Settings settings = null )
{
Show( image, null, settings, null );
}
public void Show( Texture image, CropResult onCrop, Settings settings = null, ImageResizePolicy croppedImageResizePolicy = null )
{
if( image == null )
{
Debug.LogError( "Image is null!" );
return;
}
if( !gameObject.activeSelf )
gameObject.SetActive( true );
cropCallback = onCrop;
imageResizePolicy = croppedImageResizePolicy;
if( settings == null )
settings = DefaultSettings;
m_orientedImage.texture = image;
m_originalImageSize = new Vector2( image.width, image.height );
orientedImageTransform.sizeDelta = m_originalImageSize;
MarkTextureNonReadable = settings.markTextureNonReadable;
OvalSelection = settings.ovalSelection;
GuidelinesVisibility = settings.guidelinesVisibility;
ImageBackground = settings.imageBackground;
minAspectRatio = settings.selectionMinAspectRatio;
maxAspectRatio = settings.selectionMaxAspectRatio;
if( minAspectRatio <= 0f )
minAspectRatio = 1E-6f;
if( maxAspectRatio <= 0f )
maxAspectRatio = 1E6f;
if( minAspectRatio > maxAspectRatio )
{
float temp = minAspectRatio;
minAspectRatio = maxAspectRatio;
maxAspectRatio = temp;
}
minSize = settings.selectionMinSize;
maxSize = settings.selectionMaxSize;
Vector2 maxSizeDefault = new Vector2( 2f, 2f ) * Mathf.Max( m_originalImageSize.x, m_originalImageSize.y );
if( minSize.x < 1f || minSize.y < 1f )
minSize = new Vector2( 0.1f, 0.1f ) * Mathf.Min( m_originalImageSize.x, m_originalImageSize.y );
if( maxSize.x < 1f || maxSize.y < 1f )
maxSize = maxSizeDefault;
minSize = minSize.ClampBetween( Vector2.one, Vector2.one * Mathf.Max( m_originalImageSize.x, m_originalImageSize.y ) );
maxSize = maxSize.ClampBetween( minSize, maxSizeDefault );
m_autoZoomEnabled = false;
SetOrientation( settings.initialOrientation );
m_autoZoomEnabled = settings.autoZoomEnabled;
m_pixelPerfectSelection = settings.pixelPerfectSelection;
flipHorizontalButton.SetActive( ( settings.visibleButtons & Button.FlipHorizontal ) == Button.FlipHorizontal );
flipVerticalButton.SetActive( ( settings.visibleButtons & Button.FlipVertical ) == Button.FlipVertical );
rotate90DegreesButton.SetActive( ( settings.visibleButtons & Button.Rotate90Degrees ) == Button.Rotate90Degrees );
Vector2 paddingMax = new Vector2( 1f - Mathf.Clamp01( settings.selectionInitialPaddingRight ), 1f - Mathf.Clamp01( settings.selectionInitialPaddingTop ) );
Vector2 paddingMin = new Vector2( Mathf.Clamp( settings.selectionInitialPaddingLeft, 0f, paddingMax.x ), Mathf.Clamp( settings.selectionInitialPaddingBottom, 0f, paddingMax.y ) );
UpdateSelection( Vector2.zero, m_orientedImageSize.ScaleWith( paddingMax - paddingMin ) );
m_selection.anchoredPosition = ( m_orientedImageSize - m_selection.sizeDelta ) * 0.5f;
if( m_pixelPerfectSelection )
MakePixelPerfectSelection();
ResetView( false );
}
public void Hide()
{
gameObject.SetActive( false );
m_orientedImage.texture = null;
cropCallback = null;
imageResizePolicy = null;
}
public void ResetView( bool frameSelection )
{
if( m_orientedImageSize.x <= 0f || m_orientedImageSize.y <= 0f )
return;
StopAutoZoom();
minImageScale = Mathf.Min( m_viewportSize.x / m_orientedImageSize.x, m_viewportSize.y / m_orientedImageSize.y );
m_imageHolder.anchoredPosition = RestrictImageToViewport( m_imageHolder.anchoredPosition, minImageScale );
m_imageHolder.localScale = new Vector3( minImageScale, minImageScale, minImageScale );
if( frameSelection && m_autoZoomEnabled )
StartAutoZoom( true );
}
public void Cancel()
{
if( cropCallback != null )
cropCallback( false, m_orientedImage.texture, null );
Hide();
}
public void Crop()
{
if( cropCallback != null )
{
Texture2D result = CropSelection();
cropCallback( result != null, m_orientedImage.texture, result );
}
Hide();
}
public Texture2D CropSelection()
{
Vector2 selectionSize = m_selection.sizeDelta;
int width = Mathf.Clamp( (int) selectionSize.x, 1, (int) m_orientedImageSize.x );
int height = Mathf.Clamp( (int) selectionSize.y, 1, (int) m_orientedImageSize.y );
if( imageResizePolicy != null )
imageResizePolicy( ref width, ref height );
if( width < 0 )
width = 1;
if( height < 0 )
height = 1;
return CropSelection( width, height );
}
public Texture2D CropSelection( int width, int height )
{
if( !gameObject.activeInHierarchy )
{
Debug.LogError( "Cropper is not visible!" );
return null;
}
if( m_orientedImage.texture == null )
{
Debug.LogError( "Cropper is not initialized!" );
return null;
}
// Make sure that cropped image dimensions do not exceed maximum supported texture size
int maxTextureSize = SystemInfo.maxTextureSize;
if( width > maxTextureSize || height > maxTextureSize )
{
int preferredWidth = (int) ( maxTextureSize * width / (float) height );
int preferredHeight = (int) ( maxTextureSize * height / (float) width );
if( preferredWidth <= maxTextureSize )
preferredHeight = maxTextureSize;
else
preferredWidth = maxTextureSize;
Debug.Log( string.Concat( width, "x", height, " is too large, using ", preferredWidth, "x", preferredHeight, " instead..." ) );
width = preferredWidth;
height = preferredHeight;
}
RectTransform cropRenderCanvasTR = (RectTransform) cropRenderCanvas.transform;
RectTransform cropRenderImageTR = (RectTransform) cropRenderImage.transform;
Transform cropRenderCameraTR = cropRenderCamera.transform;
cropRenderImage.texture = m_orientedImage.texture;
Vector2 selectionSize = m_selection.sizeDelta;
Vector2 selectionCenter = m_selection.anchoredPosition + selectionSize * 0.5f;
cropRenderCanvasTR.sizeDelta = m_orientedImageSize;
cropRenderSelection.anchoredPosition = m_selection.anchoredPosition;
cropRenderSelection.sizeDelta = selectionSize;
cropRenderImageTR.position = cropRenderCanvasTR.position;
cropRenderImageTR.sizeDelta = m_originalImageSize;
cropRenderImageTR.localScale = orientedImageTransform.localScale;
cropRenderImageTR.localEulerAngles = orientedImageTransform.localEulerAngles;
cropRenderCameraTR.eulerAngles = cropRenderCanvasTR.eulerAngles;
cropRenderCameraTR.position = cropRenderCanvasTR.TransformPoint( new Vector3( selectionCenter.x - m_orientedImageSize.x * 0.5f, selectionCenter.y - m_orientedImageSize.y * 0.5f, -5f ) );
cropRenderCamera.aspect = selectionSize.x / selectionSize.y;
cropRenderCamera.orthographicSize = selectionSize.y * 0.5f;
Texture2D result = null;
RenderTexture temp = RenderTexture.active;
RenderTexture renderTex = RenderTexture.GetTemporary( width, height, 24 );
try
{
cropRenderCanvas.gameObject.SetActive( true );
LayoutRebuilder.ForceRebuildLayoutImmediate( cropRenderCanvasTR );
RenderTexture.active = renderTex;
bool transparentBackground = ImageBackground.a < 1f;
if( transparentBackground )
{
cropRenderCamera.clearFlags = CameraClearFlags.Depth;
GL.Clear( false, true, ImageBackground );
}
else
cropRenderCamera.clearFlags = CameraClearFlags.Color;
cropRenderCamera.backgroundColor = ImageBackground;
cropRenderCamera.targetTexture = renderTex;
cropRenderCamera.Render();
result = new Texture2D( width, height, transparentBackground ? TextureFormat.RGBA32 : TextureFormat.RGB24, false );
result.ReadPixels( new Rect( 0, 0, width, height ), 0, 0, false );
result.Apply( false, MarkTextureNonReadable );
}
catch( System.Exception e )
{
Debug.LogException( e );
if( result != null )
{
DestroyImmediate( result );
result = null;
}
}
finally
{
RenderTexture.active = temp;
RenderTexture.ReleaseTemporary( renderTex );
cropRenderCanvas.gameObject.SetActive( false );
cropRenderImage.texture = null;
cropRenderCamera.targetTexture = null;
}
return result;
}
public void Rotate90Clockwise()
{
RotateClockwise( 1 );
}
public void Rotate180Clockwise()
{
RotateClockwise( 2 );
}
public void Rotate270Clockwise()
{
RotateClockwise( 3 );
}
private void RotateClockwise( int amount )
{
while( amount < 0 )
amount += 4;
while( amount > 3 )
amount -= 4;
if( amount == 0 )
return;
int orientationInt = (int) currentOrientation;
if( orientationInt < 4 )
{
orientationInt -= amount;
if( orientationInt < 0 )
orientationInt += 4;
}
else
{
orientationInt -= amount;
if( orientationInt < 4 )
orientationInt += 4;
}
SetOrientation( (Orientation) orientationInt );
}
public void FlipHorizontal()
{
if( currentOrientation == Orientation.Normal )
SetOrientation( Orientation.FlipHorizontal );
else if( currentOrientation == Orientation.FlipHorizontal )
SetOrientation( Orientation.Normal );
else if( currentOrientation == Orientation.Rotate90 )
SetOrientation( Orientation.Transverse );
else if( currentOrientation == Orientation.Transverse )
SetOrientation( Orientation.Rotate90 );
else if( currentOrientation == Orientation.Rotate180 )
SetOrientation( Orientation.FlipVertical );
else if( currentOrientation == Orientation.FlipVertical )
SetOrientation( Orientation.Rotate180 );
else if( currentOrientation == Orientation.Rotate270 )
SetOrientation( Orientation.Transpose );
else
SetOrientation( Orientation.Rotate270 );
}
public void FlipVertical()
{
if( currentOrientation == Orientation.Normal )
SetOrientation( Orientation.FlipVertical );
else if( currentOrientation == Orientation.FlipVertical )
SetOrientation( Orientation.Normal );
else if( currentOrientation == Orientation.Rotate90 )
SetOrientation( Orientation.Transpose );
else if( currentOrientation == Orientation.Transpose )
SetOrientation( Orientation.Rotate90 );
else if( currentOrientation == Orientation.Rotate180 )
SetOrientation( Orientation.FlipHorizontal );
else if( currentOrientation == Orientation.FlipHorizontal )
SetOrientation( Orientation.Rotate180 );
else if( currentOrientation == Orientation.Rotate270 )
SetOrientation( Orientation.Transverse );
else
SetOrientation( Orientation.Rotate270 );
}
public void SetOrientation( Orientation orientation )
{
Vector2 selectionPosition = m_selection.anchoredPosition;
Vector2 selectionSize = m_selection.sizeDelta;
if( currentOrientation == Orientation.Normal )
{
}
else if( currentOrientation == Orientation.Rotate90 )
{
selectionPosition = new Vector2( selectionPosition.y, m_orientedImageSize.x - selectionPosition.x - selectionSize.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
else if( currentOrientation == Orientation.Rotate180 )
selectionPosition = new Vector2( m_orientedImageSize.x - selectionPosition.x - selectionSize.x, m_orientedImageSize.y - selectionPosition.y - selectionSize.y );
else if( currentOrientation == Orientation.Rotate270 )
{
selectionPosition = new Vector2( m_orientedImageSize.y - selectionPosition.y - selectionSize.y, selectionPosition.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
else if( currentOrientation == Orientation.FlipHorizontal )
selectionPosition = new Vector2( m_orientedImageSize.x - selectionPosition.x - selectionSize.x, selectionPosition.y );
else if( currentOrientation == Orientation.Transpose )
{
selectionPosition = new Vector2( m_orientedImageSize.y - selectionPosition.y - selectionSize.y, m_orientedImageSize.x - selectionPosition.x - selectionSize.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
else if( currentOrientation == Orientation.FlipVertical )
selectionPosition = new Vector2( selectionPosition.x, m_orientedImageSize.y - selectionPosition.y - selectionSize.y );
else
{
selectionPosition = new Vector2( selectionPosition.y, selectionPosition.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
if( orientation == Orientation.Normal )
{
m_orientedImageSize = m_originalImageSize;
orientedImageTransform.localScale = Vector3.one;
orientedImageTransform.localEulerAngles = Vector3.zero;
}
else if( orientation == Orientation.Rotate90 )
{
m_orientedImageSize = new Vector2( m_originalImageSize.y, m_originalImageSize.x );
orientedImageTransform.localScale = Vector3.one;
orientedImageTransform.localEulerAngles = new Vector3( 0f, 0f, 90f );
selectionPosition = new Vector2( m_originalImageSize.y - selectionPosition.y - selectionSize.y, selectionPosition.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
else if( orientation == Orientation.Rotate180 )
{
m_orientedImageSize = m_originalImageSize;
orientedImageTransform.localScale = Vector3.one;
orientedImageTransform.localEulerAngles = new Vector3( 0f, 0f, 180f );
selectionPosition = new Vector2( m_originalImageSize.x - selectionPosition.x - selectionSize.x, m_originalImageSize.y - selectionPosition.y - selectionSize.y );
}
else if( orientation == Orientation.Rotate270 )
{
m_orientedImageSize = new Vector2( m_originalImageSize.y, m_originalImageSize.x );
orientedImageTransform.localScale = Vector3.one;
orientedImageTransform.localEulerAngles = new Vector3( 0f, 0f, 270f );
selectionPosition = new Vector2( selectionPosition.y, m_originalImageSize.x - selectionPosition.x - selectionSize.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
else if( orientation == Orientation.FlipHorizontal )
{
m_orientedImageSize = m_originalImageSize;
orientedImageTransform.localScale = new Vector3( -1f, 1f, 1f );
orientedImageTransform.localEulerAngles = Vector3.zero;
selectionPosition = new Vector2( m_originalImageSize.x - selectionPosition.x - selectionSize.x, selectionPosition.y );
}
else if( orientation == Orientation.Transpose )
{
m_orientedImageSize = new Vector2( m_originalImageSize.y, m_originalImageSize.x );
orientedImageTransform.localScale = new Vector3( -1f, 1f, 1f );
orientedImageTransform.localEulerAngles = new Vector3( 0f, 0f, 90f );
selectionPosition = new Vector2( m_originalImageSize.y - selectionPosition.y - selectionSize.y, m_originalImageSize.x - selectionPosition.x - selectionSize.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
else if( orientation == Orientation.FlipVertical )
{
m_orientedImageSize = m_originalImageSize;
orientedImageTransform.localScale = new Vector3( -1f, 1f, 1f );
orientedImageTransform.localEulerAngles = new Vector3( 0f, 0f, 180f );
selectionPosition = new Vector2( selectionPosition.x, m_originalImageSize.y - selectionPosition.y - selectionSize.y );
}
else
{
m_orientedImageSize = new Vector2( m_originalImageSize.y, m_originalImageSize.x );
orientedImageTransform.localScale = new Vector3( -1f, 1f, 1f );
orientedImageTransform.localEulerAngles = new Vector3( 0f, 0f, 270f );
selectionPosition = new Vector2( selectionPosition.y, selectionPosition.x );
selectionSize = new Vector2( selectionSize.y, selectionSize.x );
}
m_imageHolder.sizeDelta = m_orientedImageSize;
currMinSize = minSize.ClampBetween( Vector2.one, m_orientedImageSize );
currMaxSize = maxSize.ClampBetween( currMinSize, m_orientedImageSize );
// If new selection doesn't comply with limiting aspect ratios, keep original selection
float aspectRatio = selectionSize.x / selectionSize.y;
if( aspectRatio < minAspectRatio - 1E-4f ) // consider floating-point precision
{
// Pixel-perfect selection may not always comply with limiting aspect ratios
if( !m_pixelPerfectSelection || ( selectionSize.x + 1 ) / ( selectionSize.y - 1 ) < minAspectRatio - 1E-4f )
selectionSize = m_selection.sizeDelta;
}
else if( aspectRatio > maxAspectRatio + 1E-4f )
{
if( !m_pixelPerfectSelection || ( selectionSize.x - 1 ) / ( selectionSize.y + 1 ) > maxAspectRatio + 1E-4f )
selectionSize = m_selection.sizeDelta;
}
UpdateSelection( selectionPosition, selectionSize );
if( m_pixelPerfectSelection )
MakePixelPerfectSelection();
currentOrientation = orientation;
ResetView( true );
}
public void StartAutoZoom( bool instantZoom )
{
if( !gameObject.activeInHierarchy )
return;
StopAutoZoom();
Vector2 selectionSize = m_selection.sizeDelta;
Vector2 selectionSizeScaled = selectionSize * m_imageHolder.localScale.z;
float zoomAmount = -1f;
float scale = m_imageHolder.localScale.z;
float fillRate = Mathf.Max( selectionSizeScaled.x / m_viewportSize.x, selectionSizeScaled.y / m_viewportSize.y );
if( fillRate <= autoZoomInThreshold )
{
float scaleX = m_viewportSize.x * autoZoomInFillAmount / selectionSize.x;
float scaleY = m_viewportSize.y * autoZoomInFillAmount / selectionSize.y;
zoomAmount = Mathf.Min( scaleX, scaleY );
}
else if( fillRate >= autoZoomOutThreshold )
{
float scaleX = m_viewportSize.x * autoZoomOutFillAmount / selectionSize.x;
float scaleY = m_viewportSize.y * autoZoomOutFillAmount / selectionSize.y;
zoomAmount = Mathf.Min( scaleX, scaleY );
}
else
{
Vector2 selectionBottomLeft = m_imageHolder.anchoredPosition + m_selection.anchoredPosition * scale;
Vector2 selectionTopRight = selectionBottomLeft + m_selection.sizeDelta * scale;
// If part of the selection rests outside of the viewport, engage auto-zoom
if( selectionBottomLeft.x < -1E-4f || selectionBottomLeft.y < -1E-4f || selectionTopRight.x > m_viewportSize.x + 1E-4f || selectionTopRight.y > m_viewportSize.y + 1E-4f )
zoomAmount = scale;
}
if( zoomAmount < 0f )
return;
if( zoomAmount < minImageScale )
zoomAmount = minImageScale;
if( Mathf.Abs( zoomAmount - scale ) < 0.001f )
instantZoom = true;
autoZoomCoroutine = AutoZoom( zoomAmount, instantZoom );
StartCoroutine( autoZoomCoroutine );
}
private IEnumerator AutoZoom( float zoomAmount, bool instantZoom )
{
float elapsed = 0f;
float length = autoZoomCurve.length == 0 ? 0f : autoZoomCurve[autoZoomCurve.length - 1].time;
Vector3 initialScale = m_imageHolder.localScale;
Vector2 initialPosition = m_imageHolder.anchoredPosition;
Vector3 finalScale = new Vector3( zoomAmount, zoomAmount, zoomAmount );
Vector2 finalPosition = m_viewportSize * 0.5f - ( m_selection.anchoredPosition + m_selection.sizeDelta * 0.5f ) * finalScale.z;
finalPosition = RestrictImageToViewport( finalPosition, zoomAmount );
if( !instantZoom && elapsed < length )
{
Vector2 deltaPosition = finalPosition - initialPosition;
Vector3 deltaScale = finalScale - initialScale;
while( elapsed < length )
{
yield return null;
elapsed += Time.unscaledDeltaTime;
if( elapsed >= length )
break;
float modifier = autoZoomCurve.Evaluate( elapsed );
m_imageHolder.anchoredPosition = initialPosition + deltaPosition * modifier;
m_imageHolder.localScale = initialScale + deltaScale * modifier;
}
}
m_imageHolder.anchoredPosition = finalPosition;
m_imageHolder.localScale = finalScale;
autoZoomCoroutine = null;
}
private void StopAutoZoom()
{
if( autoZoomCoroutine != null )
{
StopCoroutine( autoZoomCoroutine );
autoZoomCoroutine = null;
}
if( currentSelectionHandler != null )
{
currentSelectionHandler.Stop();
currentSelectionHandler = null;
}
}
public bool CanModifySelectionWith( ISelectionHandler handler )
{
if( autoZoomCoroutine != null )
return false;
if( handler != currentSelectionHandler )
{
if( currentSelectionHandler != null )
currentSelectionHandler.Stop();
currentSelectionHandler = handler;
}
if( m_guidelinesVisibility == Visibility.OnDrag )
{
for( int i = 0; i < guidelines.Length; i++ )
guidelines[i].enabled = true;
}
return true;
}
public void StopModifySelectionWith( ISelectionHandler handler )
{
if( currentSelectionHandler == handler )
{
currentSelectionHandler = null;
if( m_guidelinesVisibility == Visibility.OnDrag )
{
for( int i = 0; i < guidelines.Length; i++ )
guidelines[i].enabled = false;
}
if( m_pixelPerfectSelection )
MakePixelPerfectSelection();
if( m_autoZoomEnabled )
StartAutoZoom( false );
}
}
public void MakePixelPerfectSelection()
{
Vector2 size = m_selection.sizeDelta.RoundToInt().ClampBetween( currMinSize.CeilToInt(), m_orientedImageSize );
float aspectRatio = size.x / size.y;
if( aspectRatio < minAspectRatio )
{
bool foundMatchingSize = false;
bool canExpandWidth = size.x < m_orientedImageSize.x;
bool canShrinkHeight = size.y > Mathf.CeilToInt( currMinSize.y );
if( canExpandWidth )
{
aspectRatio = ( size.x + 1 ) / size.y;
if( aspectRatio >= minAspectRatio && aspectRatio <= maxAspectRatio )
{
size.x = size.x + 1;
foundMatchingSize = true;
}
}
if( !foundMatchingSize && canShrinkHeight )
{
aspectRatio = size.x / ( size.y - 1 );
if( aspectRatio >= minAspectRatio && aspectRatio <= maxAspectRatio )
{
size.y = size.y - 1;
foundMatchingSize = true;
}
}
if( !foundMatchingSize && canExpandWidth && canShrinkHeight )
{
aspectRatio = ( size.x + 1 ) / ( size.y - 1 );
if( aspectRatio >= minAspectRatio && aspectRatio <= maxAspectRatio )
{
size.x = size.x + 1;
size.y = size.y - 1;
}
}
}
else if( aspectRatio > maxAspectRatio )
{
bool foundMatchingSize = false;
bool canShrinkWidth = size.x > Mathf.CeilToInt( currMinSize.x );
bool canExpandHeight = size.y < m_orientedImageSize.y;
if( !foundMatchingSize && canShrinkWidth )
{
aspectRatio = ( size.x - 1 ) / size.y;
if( aspectRatio >= minAspectRatio && aspectRatio <= maxAspectRatio )
{
size.x = size.x - 1;
foundMatchingSize = true;
}
}
if( canExpandHeight )
{
aspectRatio = size.x / ( size.y + 1 );
if( aspectRatio >= minAspectRatio && aspectRatio <= maxAspectRatio )
{
size.y = size.y + 1;
foundMatchingSize = true;
}
}
if( !foundMatchingSize && canShrinkWidth && canExpandHeight )
{
aspectRatio = ( size.x - 1 ) / ( size.y + 1 );
if( aspectRatio >= minAspectRatio && aspectRatio <= maxAspectRatio )
{
size.x = size.x - 1;
size.y = size.y + 1;
}
}
}
m_selection.anchoredPosition = m_selection.anchoredPosition.RoundToInt().ClampBetween( Vector2.zero, m_orientedImageSize - size );
m_selection.sizeDelta = size;
}
public void UpdateSelection( Vector2 position )
{
m_selection.anchoredPosition = position.ClampBetween( Vector2.zero, m_orientedImageSize - m_selection.sizeDelta );
}
public void UpdateSelection( Vector2 position, Vector2 size, Direction pivot = Direction.None, bool shrinkToFit = true )
{
Vector2 newSize = size.ClampBetween( currMinSize, currMaxSize );
float aspectRatio = newSize.x / newSize.y;
if( aspectRatio < minAspectRatio )
{
if( shrinkToFit )
{
float requiredHeight = newSize.x / minAspectRatio;
if( requiredHeight >= currMinSize.y )
newSize.y = requiredHeight;
else
{
float requiredWidth = currMinSize.y * minAspectRatio;
if( requiredWidth <= currMaxSize.x )
newSize = new Vector2( requiredWidth, currMinSize.y );
else
newSize = new Vector2( currMaxSize.x, currMinSize.y );
}
}
else
{
float requiredWidth = newSize.y * minAspectRatio;
if( requiredWidth <= currMaxSize.x )
newSize.x = requiredWidth;
else
{
float requiredHeight = currMaxSize.x / minAspectRatio;
if( requiredHeight <= currMaxSize.y )
newSize = new Vector2( currMaxSize.x, requiredHeight );
else
newSize = currMaxSize;
}
}
}
else if( aspectRatio > maxAspectRatio )
{
if( shrinkToFit )
{
float requiredWidth = newSize.y * maxAspectRatio;
if( requiredWidth >= currMinSize.x )
newSize.x = requiredWidth;
else
{
float requiredHeight = currMinSize.x / maxAspectRatio;
if( requiredHeight <= currMaxSize.y )
newSize = new Vector2( currMinSize.x, requiredHeight );
else
newSize = new Vector2( currMinSize.x, currMaxSize.y );
}
}
else
{
float requiredHeight = newSize.x / maxAspectRatio;
if( requiredHeight <= currMaxSize.y )
newSize.y = requiredHeight;
else
{
float requiredWidth = currMaxSize.y * maxAspectRatio;
if( requiredWidth <= currMaxSize.x )
newSize = new Vector2( requiredWidth, currMaxSize.y );
else
newSize = currMaxSize;
}
}
}
if( size.x != newSize.x )
{
if( ( pivot & Direction.Right ) == Direction.Right )
position.x -= newSize.x - size.x;
else if( ( pivot & Direction.Left ) != Direction.Left )
position.x -= ( newSize.x - size.x ) * 0.5f;
size.x = newSize.x;
}
if( size.y != newSize.y )
{
if( ( pivot & Direction.Top ) == Direction.Top )
position.y -= newSize.y - size.y;
else if( ( pivot & Direction.Bottom ) != Direction.Bottom )
position.y -= ( newSize.y - size.y ) * 0.5f;
size.y = newSize.y;
}
m_selection.anchoredPosition = position.ClampBetween( Vector2.zero, m_orientedImageSize - size );
m_selection.sizeDelta = size;
}
private Vector2 RestrictImageToViewport( Vector2 position, float scale )
{
Vector2 sizeScaled = m_imageHolder.sizeDelta * scale;
if( sizeScaled.x < m_viewportSize.x )
position.x = ( m_viewportSize.x - sizeScaled.x ) * 0.5f;
else
position.x = Mathf.Clamp( position.x, m_viewportSize.x - sizeScaled.x, 0f );
if( sizeScaled.y < m_viewportSize.y )
position.y = ( m_viewportSize.y - sizeScaled.y ) * 0.5f;
else
position.y = Mathf.Clamp( position.y, m_viewportSize.y - sizeScaled.y, 0f );
return position;
}
public Vector2 ScrollImage( Vector2 imagePosition, Direction direction )
{
if( direction == Direction.Left )
{
imagePosition.x += viewportScrollSpeed * Time.unscaledDeltaTime;
if( imagePosition.x > 0f )
imagePosition.x = 0f;
}
else if( direction == Direction.Top )
{
float imageHeight = m_imageHolder.sizeDelta.y * m_imageHolder.localScale.z;
imagePosition.y -= viewportScrollSpeed * Time.unscaledDeltaTime;
if( imagePosition.y + imageHeight < m_viewportSize.y )
imagePosition.y = m_viewportSize.y - imageHeight;
}
else if( direction == Direction.Right )
{
float imageWidth = m_imageHolder.sizeDelta.x * m_imageHolder.localScale.z;
imagePosition.x -= viewportScrollSpeed * Time.unscaledDeltaTime;
if( imagePosition.x + imageWidth < m_viewportSize.x )
imagePosition.x = m_viewportSize.x - imageWidth;
}
else
{
imagePosition.y += viewportScrollSpeed * Time.unscaledDeltaTime;
if( imagePosition.y > 0f )
imagePosition.y = 0f;
}
return imagePosition;
}
public Vector2 GetTouchPosition( Vector2 screenPos, Camera cam )
{
Vector2 localPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle( m_imageHolder, screenPos, cam, out localPos );
return localPos;
}
}