| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662 |
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Linq;
- using System.Text;
- using UnityEngine;
- using UnityEngine.Serialization;
- using UnityEngine.Sprites;
- using UnityEngine.UI;
- namespace TheraBytes.BetterUi
- {
- public enum ColorMode { Color, HorizontalGradient, VerticalGradient, }
- #if UNITY_2018_3_OR_NEWER
- [ExecuteAlways]
- #else
- [ExecuteInEditMode]
- #endif
- [HelpURL("https://documentation.therabytes.de/better-ui/BetterImage.html")]
- [AddComponentMenu("Better UI/Controls/Better Image", 30)]
- public class BetterImage : Image, IResolutionDependency, IImageAppearanceProvider
- {
- static readonly Vector2[] vertScratch = new Vector2[4];
- static readonly Vector2[] uvScratch = new Vector2[4];
- #region Nested Types
- [Serializable]
- public class SpriteSettings : IScreenConfigConnection
- {
- public Sprite Sprite;
- public ColorMode ColorMode;
- public Color PrimaryColor;
- public Color SecondaryColor;
- [SerializeField]
- string screenConfigName;
- public string ScreenConfigName { get { return screenConfigName; } set { screenConfigName = value; } }
- public SpriteSettings(Sprite sprite, ColorMode colorMode, Color primary, Color secondary)
- {
- this.Sprite = sprite;
- this.ColorMode = colorMode;
- this.PrimaryColor = primary;
- this.SecondaryColor = secondary;
- }
- }
- [Serializable]
- public class SpriteSettingsConfigCollection : SizeConfigCollection<SpriteSettings> { }
- #endregion
- #if !UNITY_2019_1_OR_NEWER
- float multipliedPixelsPerUnit => base.pixelsPerUnit;
- #endif
- public bool KeepBorderAspectRatio
- {
- get { return keepBorderAspectRatio; }
- set { keepBorderAspectRatio = value; SetVerticesDirty(); }
- }
- public Vector2SizeModifier SpriteBorderScale { get { return customBorderScales.GetCurrentItem(spriteBorderScaleFallback); } }
- public string MaterialType
- {
- get { return materialType; }
- set { ImageAppearanceProviderHelper.SetMaterialType(value, this, materialProperties, ref materialEffect, ref materialType); }
- }
- public MaterialEffect MaterialEffect
- {
- get { return materialEffect; }
- set { ImageAppearanceProviderHelper.SetMaterialEffect(value, this, materialProperties, ref materialEffect, ref materialType); }
- }
- public VertexMaterialData MaterialProperties { get { return materialProperties; } }
- public ColorMode ColoringMode
- {
- get { return colorMode; }
- set
- {
- Config.Set(value, (o) => colorMode = value, (o) => CurrentSpriteSettings.ColorMode = value);
- SetVerticesDirty();
- }
- }
- public Color SecondColor
- {
- get { return secondColor; }
- set
- {
- Config.Set(value, (o) => secondColor = value, (o) => CurrentSpriteSettings.SecondaryColor = value);
- SetVerticesDirty();
- }
- }
- public override Color color
- {
- get { return base.color; }
- set
- {
- Config.Set(value, (o) => base.color = value, (o) => CurrentSpriteSettings.PrimaryColor = value);
- }
- }
- public new Sprite sprite
- {
- get { return base.sprite; }
- set
- {
- Config.Set(value, (o) => base.sprite = value, (o) => CurrentSpriteSettings.Sprite = value);
- }
- }
- [SerializeField]
- ColorMode colorMode = ColorMode.Color;
- [SerializeField]
- Color secondColor = Color.white;
- [SerializeField]
- VertexMaterialData materialProperties = new VertexMaterialData();
- [SerializeField]
- string materialType;
- [SerializeField]
- MaterialEffect materialEffect;
- [SerializeField]
- float materialProperty1, materialProperty2, materialProperty3;
- [SerializeField]
- bool keepBorderAspectRatio;
- [FormerlySerializedAs("spriteBorderScale")]
- [SerializeField]
- Vector2SizeModifier spriteBorderScaleFallback =
- new Vector2SizeModifier(Vector2.one, Vector2.zero, 3 * Vector2.one);
- [SerializeField]
- Vector2SizeConfigCollection customBorderScales = new Vector2SizeConfigCollection();
- [SerializeField]
- SpriteSettings fallbackSpriteSettings;
- [SerializeField]
- SpriteSettingsConfigCollection customSpriteSettings = new SpriteSettingsConfigCollection();
- public SpriteSettings CurrentSpriteSettings
- {
- get
- {
- DoValidation();
- return customSpriteSettings.GetCurrentItem(fallbackSpriteSettings);
- }
- }
-
- Animator animator;
- private Sprite activeSprite => (overrideSprite != null) ? overrideSprite : sprite;
- protected override void OnEnable()
- {
- base.OnEnable();
- AssignSpriteSettings();
- animator = GetComponent<Animator>();
- if (MaterialProperties.FloatProperties != null)
- {
- if (MaterialProperties.FloatProperties.Length > 0)
- materialProperty1 = MaterialProperties.FloatProperties[0].Value;
- if (MaterialProperties.FloatProperties.Length > 1)
- materialProperty2 = MaterialProperties.FloatProperties[1].Value;
- if (MaterialProperties.FloatProperties.Length > 2)
- materialProperty3 = MaterialProperties.FloatProperties[2].Value;
- }
- }
- public float GetMaterialPropertyValue(int propertyIndex)
- {
- return ImageAppearanceProviderHelper.GetMaterialPropertyValue(propertyIndex,
- ref materialProperty1, ref materialProperty2, ref materialProperty3);
- }
- public void SetMaterialProperty(int propertyIndex, float value)
- {
- ImageAppearanceProviderHelper.SetMaterialProperty(propertyIndex, value, this, materialProperties,
- ref materialProperty1, ref materialProperty2, ref materialProperty3);
- }
- protected override void OnPopulateMesh(VertexHelper toFill)
- {
- if (animator != null
- && MaterialProperties.FloatProperties != null
- && (Application.isPlaying == animator.isActiveAndEnabled))
- {
- if (MaterialProperties.FloatProperties.Length > 0)
- MaterialProperties.FloatProperties[0].Value = materialProperty1;
- if (MaterialProperties.FloatProperties.Length > 1)
- MaterialProperties.FloatProperties[1].Value = materialProperty2;
- if (MaterialProperties.FloatProperties.Length > 2)
- MaterialProperties.FloatProperties[2].Value = materialProperty3;
- }
- if (this.activeSprite == null)
- {
- GenerateSimpleSprite(toFill, false);
- return;
- }
- switch (type)
- {
- case Type.Simple:
- GenerateSimpleSprite(toFill, base.preserveAspect);
- break;
- case Type.Sliced:
- GenerateSlicedSprite(toFill);
- break;
- case Type.Tiled:
- GenerateTiledSprite(toFill);
- break;
- case Type.Filled:
- default:
- base.OnPopulateMesh(toFill);
- break;
- }
- }
- #region Simple
- private void GenerateSimpleSprite(VertexHelper vh, bool preserveAspect)
- {
- Rect rect = GetDrawingRect(preserveAspect);
- Vector4 uv = (activeSprite == null)
- ? Vector4.zero
- : DataUtility.GetOuterUV(activeSprite);
- vh.Clear();
- AddQuad(vh, rect,
- rect.min, rect.max,
- colorMode, color, secondColor,
- new Vector2(uv.x, uv.y), new Vector2(uv.z, uv.w));
- }
- private Rect GetDrawingRect(bool shouldPreserveAspect)
- {
- Vector4 padding = (activeSprite == null) ? Vector4.zero : DataUtility.GetPadding(activeSprite);
- Vector2 spriteSize = (activeSprite == null) ? Vector2.zero : new Vector2(activeSprite.rect.width, activeSprite.rect.height);
- Rect rect = GetPixelAdjustedRect();
- if (activeSprite == null)
- return rect;
- float w = Mathf.RoundToInt(spriteSize.x);
- float h = Mathf.RoundToInt(spriteSize.y);
- Vector4 paddingFraction = new Vector4(
- padding.x / w,
- padding.y / h,
- (w - padding.z) / w,
- (h - padding.w) / h);
- if (shouldPreserveAspect && spriteSize.sqrMagnitude > 0f)
- {
- PreserveSpriteAspectRatio(ref rect, spriteSize);
- }
- var v = new Vector4(
- rect.x + rect.width * paddingFraction.x,
- rect.y + rect.height * paddingFraction.y,
- rect.x + rect.width * paddingFraction.z,
- rect.y + rect.height * paddingFraction.w);
- return new Rect(v.x, v.y, v.z - v.x, v.w - v.y);
- }
- private void PreserveSpriteAspectRatio(ref Rect rect, Vector2 spriteSize)
- {
- float spriteAspect = spriteSize.x / spriteSize.y;
- float rectAspect = rect.width / rect.height;
- if (spriteAspect > rectAspect)
- {
- float height = rect.height;
- rect.height = rect.width * (1f / spriteAspect);
- rect.y += (height - rect.height) * base.rectTransform.pivot.y;
- }
- else
- {
- float width = rect.width;
- rect.width = rect.height * spriteAspect;
- rect.x += (width - rect.width) * base.rectTransform.pivot.x;
- }
- }
- #endregion
- #region Sliced
- void GenerateSlicedSprite(VertexHelper toFill)
- {
- if (!hasBorder)
- {
- base.OnPopulateMesh(toFill);
- return;
- }
- Vector4 outer, inner, padding, border;
- if (activeSprite != null)
- {
- outer = DataUtility.GetOuterUV(activeSprite);
- inner = DataUtility.GetInnerUV(activeSprite);
- padding = DataUtility.GetPadding(activeSprite);
- border = activeSprite.border;
- }
- else
- {
- outer = Vector4.zero;
- inner = Vector4.zero;
- padding = Vector4.zero;
- border = Vector4.zero;
- }
- Vector2 scale = SpriteBorderScale.CalculateSize(this) / multipliedPixelsPerUnit;
- Rect rect = GetPixelAdjustedRect();
- border = new Vector4(
- scale.x * border.x,
- scale.y * border.y,
- scale.x * border.z,
- scale.y * border.w);
- border = GetAdjustedBorders(border, rect, KeepBorderAspectRatio,
- new Vector2(
- activeSprite.rect.width * scale.x,
- activeSprite.rect.height * scale.y) );
- if ((border.x + border.z) > rect.width)
- {
- float s = rect.width / (border.x + border.z);
- border.x *= s;
- border.z *= s;
- }
- if (border.y + border.w > rect.height)
- {
- float s = rect.height / (border.y + border.w);
- border.y *= s;
- border.w *= s;
- }
- padding = padding / multipliedPixelsPerUnit;
- vertScratch[0] = new Vector2(padding.x, padding.y);
- vertScratch[3] = new Vector2(rect.width - padding.z, rect.height - padding.w);
- vertScratch[1].x = border.x;
- vertScratch[1].y = border.y;
- vertScratch[2].x = rect.width - border.z;
- vertScratch[2].y = rect.height - border.w;
- for (int i = 0; i < 4; i++)
- {
- vertScratch[i].x += rect.x;
- vertScratch[i].y += rect.y;
- }
- uvScratch[0] = new Vector2(outer.x, outer.y);
- uvScratch[1] = new Vector2(inner.x, inner.y);
- uvScratch[2] = new Vector2(inner.z, inner.w);
- uvScratch[3] = new Vector2(outer.z, outer.w);
- toFill.Clear();
- for (int x = 0; x < 3; x++)
- {
- int xIdx = x + 1;
- for (int y = 0; y < 3; y++)
- {
- if (this.fillCenter || x != 1 || y != 1)
- {
- int yIdx = y + 1;
- AddQuad(toFill,
- posMin: new Vector2(vertScratch[x].x, vertScratch[y].y),
- posMax: new Vector2(vertScratch[xIdx].x, vertScratch[yIdx].y),
- bounds: rect,
- mode: colorMode, colorA: color, colorB: secondColor,
- uvMin: new Vector2(uvScratch[x].x, uvScratch[y].y),
- uvMax: new Vector2(uvScratch[xIdx].x, uvScratch[yIdx].y));
- }
- }
- }
- }
- #endregion
- #region Tiled
- private void GenerateTiledSprite(VertexHelper toFill)
- {
- Vector4 outerUV, innerUV, border;
- Vector2 spriteSize;
- if (activeSprite == null)
- {
- outerUV = Vector4.zero;
- innerUV = Vector4.zero;
- border = Vector4.zero;
- spriteSize = Vector2.one * 100f;
- }
- else
- {
- outerUV = DataUtility.GetOuterUV(activeSprite);
- innerUV = DataUtility.GetInnerUV(activeSprite);
- border = activeSprite.border;
- spriteSize = activeSprite.rect.size;
- }
- Rect rect = base.GetPixelAdjustedRect();
- float tileWidth = (spriteSize.x - border.x - border.z) / multipliedPixelsPerUnit;
- float tileHeight = (spriteSize.y - border.y - border.w) / multipliedPixelsPerUnit;
- border = this.GetAdjustedBorders(border / multipliedPixelsPerUnit, rect, false,
- new Vector2(
- activeSprite.textureRect.width,
- activeSprite.textureRect.height));
- Vector2 scale = SpriteBorderScale.CalculateSize(this);
- tileWidth *= scale.x;
- tileHeight *= scale.y;
- Vector2 uvMin = new Vector2(innerUV.x, innerUV.y);
- Vector2 uvMax = new Vector2(innerUV.z, innerUV.w);
- UIVertex.simpleVert.color = this.color;
- // Min to max max range for tiled region in coordinates relative to lower left corner.
- float xMin = scale.x * border.x;
- float xMax = rect.width - (scale.x * border.z);
- float yMin = scale.y * border.y;
- float yMax = rect.height - (scale.y * border.w);
- toFill.Clear();
- Vector2 uvMax2 = uvMax;
- Vector2 pos = rect.position;
- if (tileWidth <= 0f)
- {
- tileWidth = xMax - xMin;
- }
- if (tileHeight <= 0f)
- {
- tileHeight = yMax - yMin;
- }
- if (this.fillCenter)
- {
- for (float y1 = yMin; y1 < yMax; y1 = y1 + tileHeight)
- {
- float y2 = y1 + tileHeight;
- if (y2 > yMax)
- {
- uvMax2.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1);
- y2 = yMax;
- }
- uvMax2.x = uvMax.x;
- for (float x1 = xMin; x1 < xMax; x1 = x1 + tileWidth)
- {
- float x2 = x1 + tileWidth;
- if (x2 > xMax)
- {
- uvMax2.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1);
- x2 = xMax;
- }
- AddQuad(toFill, rect,
- new Vector2(x1, y1) + pos,
- new Vector2(x2, y2) + pos,
- colorMode, color, secondColor,
- uvMin, uvMax2);
- }
- }
- }
- if (this.hasBorder)
- {
- uvMax2 = uvMax;
- for (float y1 = yMin; y1 < yMax; y1 = y1 + tileHeight)
- {
- float y2 = y1 + tileHeight;
- if (y2 > yMax)
- {
- uvMax2.y = uvMin.y + (uvMax.y - uvMin.y) * (yMax - y1) / (y2 - y1);
- y2 = yMax;
- }
- AddQuad(toFill, rect,
- new Vector2(0f, y1) + pos,
- new Vector2(xMin, y2) + pos,
- colorMode, color, secondColor,
- new Vector2(outerUV.x, uvMin.y), new Vector2(uvMin.x, uvMax2.y));
- AddQuad(toFill, rect,
- new Vector2(xMax, y1) + pos,
- new Vector2(rect.width, y2) + pos,
- colorMode, color, secondColor,
- new Vector2(uvMax.x, uvMin.y), new Vector2(outerUV.z, uvMax2.y));
- }
- uvMax2 = uvMax;
- for (float x1 = xMin; x1 < xMax; x1 = x1 + tileWidth)
- {
- float x2 = x1 + tileWidth;
- if (x2 > xMax)
- {
- uvMax2.x = uvMin.x + (uvMax.x - uvMin.x) * (xMax - x1) / (x2 - x1);
- x2 = xMax;
- }
- AddQuad(toFill, rect,
- new Vector2(x1, 0f) + pos,
- new Vector2(x2, yMin) + pos,
- colorMode, color, secondColor,
- new Vector2(uvMin.x, outerUV.y), new Vector2(uvMax2.x, uvMin.y));
- AddQuad(toFill, rect,
- new Vector2(x1, yMax) + pos,
- new Vector2(x2, rect.height) + pos,
- colorMode, color, secondColor,
- new Vector2(uvMin.x, uvMax.y), new Vector2(uvMax2.x, outerUV.w));
- }
- AddQuad(toFill, rect,
- new Vector2(0f, 0f) + pos,
- new Vector2(xMin, yMin) + pos,
- colorMode, color, secondColor,
- new Vector2(outerUV.x, outerUV.y), new Vector2(uvMin.x, uvMin.y));
- AddQuad(toFill, rect,
- new Vector2(xMax, 0f) + pos,
- new Vector2(rect.width, yMin) + pos,
- colorMode, color, secondColor,
- new Vector2(uvMax.x, outerUV.y), new Vector2(outerUV.z, uvMin.y));
- AddQuad(toFill, rect,
- new Vector2(0f, yMax) + pos,
- new Vector2(xMin, rect.height) + pos,
- colorMode, color, secondColor,
- new Vector2(outerUV.x, uvMax.y), new Vector2(uvMin.x, outerUV.w));
- AddQuad(toFill, rect,
- new Vector2(xMax, yMax) + pos,
- new Vector2(rect.width, rect.height) + pos,
- colorMode, color, secondColor,
- new Vector2(uvMax.x, uvMax.y), new Vector2(outerUV.z, outerUV.w));
- }
- }
- #endregion
- private void AddQuad(
- VertexHelper vertexHelper, Rect bounds,
- Vector2 posMin, Vector2 posMax,
- ColorMode mode, Color colorA, Color colorB,
- Vector2 uvMin, Vector2 uvMax)
- {
- ImageAppearanceProviderHelper.AddQuad(vertexHelper, bounds, posMin, posMax, mode, colorA, colorB, uvMin, uvMax, materialProperties);
- }
-
- Vector4 GetAdjustedBorders(Vector4 border, Rect rect, bool keepAspect, Vector2 texSize)
- {
- float scale = 1;
- for (int axis = 0; axis <= 1; axis++)
- {
- // If the rect is smaller than the combined borders, then there's not room for the borders at their normal size.
- // In order to avoid artefacts with overlapping borders, we scale the borders down to fit.
- float combinedBorders = border[axis] + border[axis + 2];
- if (rect.size[axis] < combinedBorders)// && combinedBorders != 0)
- {
- if(keepAspect)
- {
- scale = Mathf.Min(scale, rect.size[axis] / combinedBorders);
- }
- else
- {
- float borderScaleRatio = rect.size[axis] / combinedBorders;
- border[axis] *= borderScaleRatio;
- border[axis + 2] *= borderScaleRatio;
- }
- }
- else if (combinedBorders == 0 && keepAspect)
- {
- int o = (axis + 1) % 2;
- combinedBorders = border[o] + border[o + 2];
- scale = rect.size[axis] / texSize[axis];
- if(scale * combinedBorders > rect.size[o])
- {
- scale = rect.size[o] / combinedBorders;
- }
- }
- }
- if (keepAspect)
- {
- border = scale * border;
- }
- return border;
- }
- public void OnResolutionChanged()
- {
- SetVerticesDirty();
- AssignSpriteSettings();
- }
- private void AssignSpriteSettings()
- {
- var settings = CurrentSpriteSettings;
- this.sprite = settings.Sprite;
- this.colorMode = settings.ColorMode;
- this.color = settings.PrimaryColor;
- this.secondColor = settings.SecondaryColor;
- }
- #if UNITY_EDITOR
- protected override void OnValidate()
- {
- base.OnValidate();
- DoValidation();
- AssignSpriteSettings();
- }
- #endif
- private void DoValidation()
- {
- bool isUnInitialized = fallbackSpriteSettings == null
- || (fallbackSpriteSettings.Sprite == null
- && fallbackSpriteSettings.ColorMode == ColorMode.Color
- && fallbackSpriteSettings.PrimaryColor == new Color());
- if (isUnInitialized)
- {
- fallbackSpriteSettings = new SpriteSettings(this.sprite, this.colorMode, this.color, this.secondColor);
- }
- }
- }
- }
|