mirror of
https://github.com/drewcassidy/KSP-Conformal-Decals.git
synced 2024-09-01 18:23:54 +00:00
Documentation and cleanup pass
This commit is contained in:
parent
5a18943db9
commit
1859e51a2e
Binary file not shown.
@ -100,7 +100,6 @@
|
|||||||
<Compile Include="UI\ColorChannelSlider.cs" />
|
<Compile Include="UI\ColorChannelSlider.cs" />
|
||||||
<Compile Include="Util/Logging.cs" />
|
<Compile Include="Util/Logging.cs" />
|
||||||
<Compile Include="Util/OrientedBounds.cs" />
|
<Compile Include="Util/OrientedBounds.cs" />
|
||||||
<Compile Include="Util/TextureUtils.cs" />
|
|
||||||
<Compile Include="Util/ParseUtil.cs" />
|
<Compile Include="Util/ParseUtil.cs" />
|
||||||
<Compile Include="UI/BoxSlider.cs" />
|
<Compile Include="UI/BoxSlider.cs" />
|
||||||
<Compile Include="Util\ColorHSL.cs" />
|
<Compile Include="Util\ColorHSL.cs" />
|
||||||
|
@ -6,13 +6,17 @@ using UniLinq;
|
|||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
public class DecalFont : IEquatable<DecalFont> {
|
public class DecalFont : IEquatable<DecalFont> {
|
||||||
|
/// Human-readable name for the font
|
||||||
public string Title { get; }
|
public string Title { get; }
|
||||||
|
|
||||||
public TMP_FontAsset FontAsset { get; }
|
/// Internal name for the font
|
||||||
|
|
||||||
public string Name => FontAsset.name;
|
public string Name => FontAsset.name;
|
||||||
|
|
||||||
|
/// The font asset itself
|
||||||
|
public TMP_FontAsset FontAsset { get; }
|
||||||
|
|
||||||
|
/// Styles that are forced on for this font,
|
||||||
|
/// e.g. smallcaps for a font without lower case characters
|
||||||
public FontStyles FontStyle { get; }
|
public FontStyles FontStyle { get; }
|
||||||
|
|
||||||
public bool Bold => (FontStyle & FontStyles.Bold) != 0;
|
public bool Bold => (FontStyle & FontStyles.Bold) != 0;
|
||||||
@ -23,7 +27,8 @@ namespace ConformalDecals.Text {
|
|||||||
|
|
||||||
public bool SmallCaps => (FontStyle & FontStyles.SmallCaps) != 0;
|
public bool SmallCaps => (FontStyle & FontStyles.SmallCaps) != 0;
|
||||||
|
|
||||||
|
/// Styles that are forced off for this font,
|
||||||
|
/// e.g. underline for a font with no underscore character
|
||||||
public FontStyles FontStyleMask { get; }
|
public FontStyles FontStyleMask { get; }
|
||||||
|
|
||||||
public bool BoldMask => (FontStyleMask & FontStyles.Bold) != 0;
|
public bool BoldMask => (FontStyleMask & FontStyles.Bold) != 0;
|
||||||
|
@ -3,12 +3,16 @@ using System.Text.RegularExpressions;
|
|||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
public class DecalText : IEquatable<DecalText> {
|
public class DecalText : IEquatable<DecalText> {
|
||||||
|
/// Raw text contents
|
||||||
public string Text { get; }
|
public string Text { get; }
|
||||||
|
|
||||||
|
/// Font asset used by this text snippet
|
||||||
public DecalFont Font { get; }
|
public DecalFont Font { get; }
|
||||||
|
|
||||||
|
/// Style used by this text snippet
|
||||||
public DecalTextStyle Style { get; }
|
public DecalTextStyle Style { get; }
|
||||||
|
|
||||||
|
/// The text formatted with newlines for vertical text
|
||||||
public string FormattedText {
|
public string FormattedText {
|
||||||
get {
|
get {
|
||||||
if (Style.Vertical) {
|
if (Style.Vertical) {
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using TMPro;
|
using TMPro;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
// ReSharper disable NonReadonlyMemberInGetHashCode
|
// ReSharper disable NonReadonlyMemberInGetHashCode
|
||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
public struct DecalTextStyle : IEquatable<DecalTextStyle> {
|
public struct DecalTextStyle : IEquatable<DecalTextStyle> {
|
||||||
private FontStyles _fontStyle;
|
private FontStyles _fontStyle;
|
||||||
private bool _vertical;
|
private bool _vertical;
|
||||||
private float _lineSpacing;
|
private float _lineSpacing;
|
||||||
private float _charSpacing;
|
private float _charSpacing;
|
||||||
|
|
||||||
public FontStyles FontStyle {
|
public FontStyles FontStyle {
|
||||||
get => _fontStyle;
|
get => _fontStyle;
|
||||||
@ -61,7 +62,7 @@ namespace ConformalDecals.Text {
|
|||||||
get => _charSpacing;
|
get => _charSpacing;
|
||||||
set => _charSpacing = value;
|
set => _charSpacing = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DecalTextStyle(FontStyles fontStyle, bool vertical, float lineSpacing, float charSpacing) {
|
public DecalTextStyle(FontStyles fontStyle, bool vertical, float lineSpacing, float charSpacing) {
|
||||||
_fontStyle = fontStyle;
|
_fontStyle = fontStyle;
|
||||||
_vertical = vertical;
|
_vertical = vertical;
|
||||||
@ -70,7 +71,7 @@ namespace ConformalDecals.Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(DecalTextStyle other) {
|
public bool Equals(DecalTextStyle other) {
|
||||||
return FontStyle == other.FontStyle && Vertical == other.Vertical &&
|
return FontStyle == other.FontStyle && Vertical == other.Vertical &&
|
||||||
Mathf.Approximately(LineSpacing, other.LineSpacing) &&
|
Mathf.Approximately(LineSpacing, other.LineSpacing) &&
|
||||||
Mathf.Approximately(CharSpacing, other.CharSpacing);
|
Mathf.Approximately(CharSpacing, other.CharSpacing);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using UniLinq;
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
|
/// KSP database loader for KSPFont files which contain TextMeshPro font assets
|
||||||
[DatabaseLoaderAttrib(new[] {"kspfont"})]
|
[DatabaseLoaderAttrib(new[] {"kspfont"})]
|
||||||
public class FontLoader : DatabaseLoader<GameDatabase.TextureInfo> {
|
public class FontLoader : DatabaseLoader<GameDatabase.TextureInfo> {
|
||||||
private const string FallbackName = "NotoSans-Regular SDF";
|
private const string FallbackName = "NotoSans-Regular SDF";
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
|
/// Texture render output, used for cacheing and is the datastructure returned to the ModuleConformalText class
|
||||||
public class TextRenderOutput {
|
public class TextRenderOutput {
|
||||||
|
/// Texture with the rendered text
|
||||||
public Texture2D Texture { get; private set; }
|
public Texture2D Texture { get; private set; }
|
||||||
|
|
||||||
|
/// The rectangle that the rendered text takes up within the texture
|
||||||
public Rect Window { get; private set; }
|
public Rect Window { get; private set; }
|
||||||
|
|
||||||
|
/// The number of users for this render output. If 0, it can be discarded from the cache and the texture reused
|
||||||
public int UserCount { get; set; }
|
public int UserCount { get; set; }
|
||||||
|
|
||||||
public TextRenderOutput(Texture2D texture, Rect window) {
|
public TextRenderOutput(Texture2D texture, Rect window) {
|
||||||
|
@ -5,11 +5,21 @@ using UnityEngine;
|
|||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
|
|
||||||
namespace ConformalDecals.Text {
|
namespace ConformalDecals.Text {
|
||||||
|
// TODO: Testing shows the job system is unnecessary, so remove job system code.
|
||||||
|
|
||||||
|
/// Class handing text rendering.
|
||||||
|
/// Is a singleton referencing a single gameobject in the scene which contains the TextMeshPro component
|
||||||
[KSPAddon(KSPAddon.Startup.Instantly, true)]
|
[KSPAddon(KSPAddon.Startup.Instantly, true)]
|
||||||
public class TextRenderer : MonoBehaviour {
|
public class TextRenderer : MonoBehaviour {
|
||||||
public const TextureFormat TextTextureFormat = TextureFormat.RG16;
|
/// Texture format used for returned textures.
|
||||||
|
/// Unfortunately due to how Unity textures work, this cannot be R8 or Alpha8,
|
||||||
|
/// so theres always a superfluous green channel using memory
|
||||||
|
public const TextureFormat TextTextureFormat = TextureFormat.RG16;
|
||||||
|
|
||||||
|
/// Render Texture format used when rendering
|
||||||
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
public const RenderTextureFormat TextRenderTextureFormat = RenderTextureFormat.R8;
|
||||||
|
|
||||||
|
/// The text renderer object within the scene which contains the TextMeshPro component used for rendering.
|
||||||
public static TextRenderer Instance {
|
public static TextRenderer Instance {
|
||||||
get {
|
get {
|
||||||
if (!_instance._isSetup) {
|
if (!_instance._isSetup) {
|
||||||
@ -20,6 +30,7 @@ namespace ConformalDecals.Text {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Text Render unityevent, used with the job system to signal render completion
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class TextRenderEvent : UnityEvent<TextRenderOutput> { }
|
public class TextRenderEvent : UnityEvent<TextRenderOutput> { }
|
||||||
|
|
||||||
@ -37,7 +48,7 @@ namespace ConformalDecals.Text {
|
|||||||
private static readonly Dictionary<DecalText, TextRenderOutput> RenderCache = new Dictionary<DecalText, TextRenderOutput>();
|
private static readonly Dictionary<DecalText, TextRenderOutput> RenderCache = new Dictionary<DecalText, TextRenderOutput>();
|
||||||
private static readonly Queue<TextRenderJob> RenderJobs = new Queue<TextRenderJob>();
|
private static readonly Queue<TextRenderJob> RenderJobs = new Queue<TextRenderJob>();
|
||||||
|
|
||||||
// Update text using the job queue
|
/// Update text using the job queue
|
||||||
public static TextRenderJob UpdateText(DecalText oldText, DecalText newText, UnityAction<TextRenderOutput> renderFinishedCallback) {
|
public static TextRenderJob UpdateText(DecalText oldText, DecalText newText, UnityAction<TextRenderOutput> renderFinishedCallback) {
|
||||||
if (newText == null) throw new ArgumentNullException(nameof(newText));
|
if (newText == null) throw new ArgumentNullException(nameof(newText));
|
||||||
|
|
||||||
@ -46,14 +57,14 @@ namespace ConformalDecals.Text {
|
|||||||
return job;
|
return job;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update text immediately without using job queue
|
/// Update text immediately without using job queue
|
||||||
public static TextRenderOutput UpdateTextNow(DecalText oldText, DecalText newText) {
|
public static TextRenderOutput UpdateTextNow(DecalText oldText, DecalText newText) {
|
||||||
if (newText == null) throw new ArgumentNullException(nameof(newText));
|
if (newText == null) throw new ArgumentNullException(nameof(newText));
|
||||||
|
|
||||||
return Instance.RunJob(new TextRenderJob(oldText, newText, null), out _);
|
return Instance.RunJob(new TextRenderJob(oldText, newText, null), out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unregister a user of a piece of text
|
/// Unregister a user of a piece of text
|
||||||
public static void UnregisterText(DecalText text) {
|
public static void UnregisterText(DecalText text) {
|
||||||
Debug.Log($"[ConformalDecals] Unregistering text '{text.Text}'");
|
Debug.Log($"[ConformalDecals] Unregistering text '{text.Text}'");
|
||||||
if (RenderCache.TryGetValue(text, out var renderedText)) {
|
if (RenderCache.TryGetValue(text, out var renderedText)) {
|
||||||
@ -75,6 +86,7 @@ namespace ConformalDecals.Text {
|
|||||||
DontDestroyOnLoad(gameObject);
|
DontDestroyOnLoad(gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Setup this text renderer instance for rendering
|
||||||
private void Setup() {
|
private void Setup() {
|
||||||
if (_isSetup) return;
|
if (_isSetup) return;
|
||||||
|
|
||||||
@ -89,7 +101,7 @@ namespace ConformalDecals.Text {
|
|||||||
_isSetup = true;
|
_isSetup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run a text render job
|
/// Run a text render job
|
||||||
private TextRenderOutput RunJob(TextRenderJob job, out bool renderNeeded) {
|
private TextRenderOutput RunJob(TextRenderJob job, out bool renderNeeded) {
|
||||||
if (!job.Needed) {
|
if (!job.Needed) {
|
||||||
renderNeeded = false;
|
renderNeeded = false;
|
||||||
@ -100,6 +112,7 @@ namespace ConformalDecals.Text {
|
|||||||
foreach (var cacheitem in RenderCache) {
|
foreach (var cacheitem in RenderCache) {
|
||||||
Debug.Log($"[ConformalDecals] Cache item: '{cacheitem.Key.Text}' with {cacheitem.Value.UserCount} users");
|
Debug.Log($"[ConformalDecals] Cache item: '{cacheitem.Key.Text}' with {cacheitem.Value.UserCount} users");
|
||||||
}
|
}
|
||||||
|
|
||||||
job.Start();
|
job.Start();
|
||||||
|
|
||||||
Texture2D texture = null;
|
Texture2D texture = null;
|
||||||
@ -134,16 +147,16 @@ namespace ConformalDecals.Text {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderOutput.UserCount++;
|
renderOutput.UserCount++;
|
||||||
|
|
||||||
job.Finish(renderOutput);
|
job.Finish(renderOutput);
|
||||||
return renderOutput;
|
return renderOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render a piece of text to a given texture
|
/// Render a piece of text to a given texture
|
||||||
public TextRenderOutput RenderText(DecalText text, Texture2D texture) {
|
public TextRenderOutput RenderText(DecalText text, Texture2D texture) {
|
||||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||||
if (_tmp == null) throw new InvalidOperationException("TextMeshPro object not yet created.");
|
if (_tmp == null) throw new InvalidOperationException("TextMeshPro object not yet created.");
|
||||||
|
|
||||||
// SETUP TMP OBJECT FOR RENDERING
|
// SETUP TMP OBJECT FOR RENDERING
|
||||||
_tmp.text = text.FormattedText;
|
_tmp.text = text.FormattedText;
|
||||||
_tmp.font = text.Font.FontAsset;
|
_tmp.font = text.Font.FontAsset;
|
||||||
@ -171,13 +184,13 @@ namespace ConformalDecals.Text {
|
|||||||
// SETUP MATERIALS AND BOUNDS
|
// SETUP MATERIALS AND BOUNDS
|
||||||
for (int i = 0; i < meshFilters.Length; i++) {
|
for (int i = 0; i < meshFilters.Length; i++) {
|
||||||
var renderer = meshFilters[i].gameObject.GetComponent<MeshRenderer>();
|
var renderer = meshFilters[i].gameObject.GetComponent<MeshRenderer>();
|
||||||
|
|
||||||
meshes[i] = meshFilters[i].mesh;
|
meshes[i] = meshFilters[i].mesh;
|
||||||
if (i == 0) meshes[i] = _tmp.mesh;
|
if (i == 0) meshes[i] = _tmp.mesh;
|
||||||
|
|
||||||
materials[i] = Instantiate(renderer.material);
|
materials[i] = Instantiate(renderer.material);
|
||||||
materials[i].shader = _blitShader;
|
materials[i].shader = _blitShader;
|
||||||
|
|
||||||
if (renderer == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has filter but no renderer");
|
if (renderer == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has filter but no renderer");
|
||||||
if (meshes[i] == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has a null mesh");
|
if (meshes[i] == null) throw new FormatException($"Object {meshFilters[i].gameObject.name} has a null mesh");
|
||||||
|
|
||||||
|
@ -1,263 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace ConformalDecals.Util {
|
|
||||||
public static class TextureUtils {
|
|
||||||
public enum BlitMode {
|
|
||||||
Set,
|
|
||||||
Add,
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Color32 AddColor32(Color32 color1, Color32 color2) {
|
|
||||||
return new Color32((byte) (color1.r + color2.r), (byte) (color1.g + color2.g), (byte) (color1.b + color2.b), (byte) (color1.a + color2.a));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Color32 AddColor32Clamped(Color32 color1, Color32 color2) {
|
|
||||||
var r = color1.r + color2.r;
|
|
||||||
var g = color1.g + color2.g;
|
|
||||||
var b = color1.b + color2.b;
|
|
||||||
var a = color1.a + color2.a;
|
|
||||||
if (r > byte.MaxValue) r = byte.MaxValue;
|
|
||||||
if (g > byte.MaxValue) g = byte.MaxValue;
|
|
||||||
if (b > byte.MaxValue) b = byte.MaxValue;
|
|
||||||
if (a > byte.MaxValue) a = byte.MaxValue;
|
|
||||||
|
|
||||||
return new Color32((byte) r, (byte) g, (byte) b, (byte) a);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ClearTexture(Color32[] colors, Color32 clearColor = default) {
|
|
||||||
for (var i = 0; i < colors.Length; i++) {
|
|
||||||
colors[i] = clearColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BlitRectAlpha(
|
|
||||||
Texture2D src, Color32[] srcColors, Vector2Int srcPos,
|
|
||||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos,
|
|
||||||
Vector2Int size, BlitMode mode) {
|
|
||||||
|
|
||||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref size);
|
|
||||||
|
|
||||||
if (size.x <= 0 || size.y <= 0) return;
|
|
||||||
|
|
||||||
int srcIndex = srcPos.x + srcPos.y * src.width;
|
|
||||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
|
||||||
|
|
||||||
for (int dy = size.y - 1; dy >= 0; dy--) {
|
|
||||||
|
|
||||||
for (int dx = size.x - 1; dx >= 0; dx--) {
|
|
||||||
switch (mode) {
|
|
||||||
case BlitMode.Set:
|
|
||||||
dstColors[dstIndex + dx].a = srcColors[srcIndex + dx].a;
|
|
||||||
break;
|
|
||||||
case BlitMode.Add:
|
|
||||||
var s = srcColors[srcIndex + dx].a;
|
|
||||||
var d = dstColors[dstIndex + dx].a;
|
|
||||||
var sum = s + d;
|
|
||||||
if (sum > byte.MaxValue) sum = byte.MaxValue;
|
|
||||||
dstColors[dstIndex + dx].a = (byte) sum;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srcIndex += src.width;
|
|
||||||
dstIndex += dst.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BlitRect(
|
|
||||||
Texture2D src, Color32[] srcColors, Vector2Int srcPos,
|
|
||||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos,
|
|
||||||
Vector2Int size, BlitMode mode) {
|
|
||||||
|
|
||||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref size);
|
|
||||||
|
|
||||||
if (size.x <= 0 || size.y <= 0) return;
|
|
||||||
|
|
||||||
int srcIndex = srcPos.x + srcPos.y * src.width;
|
|
||||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
|
||||||
|
|
||||||
for (int dy = 0; dy < size.y; dy++) {
|
|
||||||
|
|
||||||
for (int dx = 0; dx < size.x; dx++) {
|
|
||||||
switch (mode) {
|
|
||||||
case BlitMode.Set:
|
|
||||||
dstColors[dstIndex + dx] = srcColors[srcIndex + dx];
|
|
||||||
break;
|
|
||||||
case BlitMode.Add:
|
|
||||||
dstColors[dstIndex + dx] = AddColor32Clamped(srcColors[srcIndex + dx], dstColors[dstIndex + dx]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
srcIndex += src.width;
|
|
||||||
dstIndex += dst.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BlitRectBilinearAlpha(
|
|
||||||
Texture2D src, Vector2Int srcPos, Vector2 srcSize,
|
|
||||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos, Vector2Int dstSize,
|
|
||||||
BlitMode mode) {
|
|
||||||
|
|
||||||
var sizeRatio = dstSize / srcSize;
|
|
||||||
|
|
||||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref srcSize, ref dstSize);
|
|
||||||
|
|
||||||
if (dstSize.x <= 0 || dstSize.y <= 0) return;
|
|
||||||
|
|
||||||
var srcPixel = new Vector2(1.0f / src.width, 1.0f / src.height);
|
|
||||||
var srcStart = (srcPos * srcPixel) + (srcPixel / 2);
|
|
||||||
var srcStep = sizeRatio * srcPixel;
|
|
||||||
var srcY = srcStart.y;
|
|
||||||
|
|
||||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
|
||||||
for (int dy = 0;
|
|
||||||
dy < dstSize.y;
|
|
||||||
dy++) {
|
|
||||||
var srcX = srcStart.x;
|
|
||||||
|
|
||||||
for (int dx = 0; dx < dstSize.x; dx++) {
|
|
||||||
switch (mode) {
|
|
||||||
case BlitMode.Set:
|
|
||||||
dstColors[dstIndex + dx].a = (byte) (src.GetPixelBilinear(srcX, srcY).a * byte.MaxValue);
|
|
||||||
break;
|
|
||||||
case BlitMode.Add:
|
|
||||||
var s = (byte) (src.GetPixelBilinear(srcX, srcY).a * byte.MaxValue);
|
|
||||||
var d = dstColors[dstIndex + dx].a;
|
|
||||||
var sum = s + d;
|
|
||||||
if (sum > byte.MaxValue) sum = byte.MaxValue;
|
|
||||||
dstColors[dstIndex + dx].a = (byte) sum;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
srcX += srcStep.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
srcY += srcStep.y;
|
|
||||||
dstIndex += dst.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void BlitRectBilinear(
|
|
||||||
Texture2D src, Vector2Int srcPos, Vector2 srcSize,
|
|
||||||
Texture2D dst, Color32[] dstColors, Vector2Int dstPos, Vector2Int dstSize,
|
|
||||||
BlitMode mode) {
|
|
||||||
|
|
||||||
var sizeRatio = dstSize / srcSize;
|
|
||||||
|
|
||||||
ClipRect(src, ref srcPos, dst, ref dstPos, ref srcSize, ref dstSize);
|
|
||||||
|
|
||||||
if (dstSize.x <= 0 || dstSize.y <= 0) return;
|
|
||||||
|
|
||||||
var srcPixel = new Vector2(1.0f / src.width, 1.0f / src.height);
|
|
||||||
var srcStart = (srcPos * srcPixel) + (srcPixel / 2);
|
|
||||||
var srcStep = sizeRatio * srcPixel;
|
|
||||||
var srcY = srcStart.y;
|
|
||||||
|
|
||||||
int dstIndex = dstPos.x + dstPos.y * dst.width;
|
|
||||||
for (int dy = 0;
|
|
||||||
dy < dstSize.y;
|
|
||||||
dy++) {
|
|
||||||
var srcX = srcStart.x;
|
|
||||||
|
|
||||||
for (int dx = 0; dx < dstSize.x; dx++) {
|
|
||||||
switch (mode) {
|
|
||||||
case BlitMode.Set:
|
|
||||||
dstColors[dstIndex + dx] = src.GetPixelBilinear(srcX, srcY);
|
|
||||||
break;
|
|
||||||
case BlitMode.Add:
|
|
||||||
dstColors[dstIndex + dx] = AddColor32Clamped(src.GetPixelBilinear(srcX, srcY), dstColors[dstIndex + dx]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
srcX += srcStep.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
srcY += srcStep.y;
|
|
||||||
dstIndex += dst.width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ClipRect(Texture2D src, ref Vector2Int srcPos, Texture2D dst, ref Vector2Int dstPos, ref Vector2Int size) {
|
|
||||||
if (srcPos.x < 0) {
|
|
||||||
size.x += srcPos.x;
|
|
||||||
dstPos.x -= srcPos.x;
|
|
||||||
srcPos.x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPos.y < 0) {
|
|
||||||
size.y += srcPos.y;
|
|
||||||
dstPos.y -= srcPos.y;
|
|
||||||
srcPos.y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dstPos.x < 0) {
|
|
||||||
size.x += dstPos.x;
|
|
||||||
srcPos.x -= dstPos.x;
|
|
||||||
dstPos.x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dstPos.y < 0) {
|
|
||||||
size.y += dstPos.y;
|
|
||||||
srcPos.y -= dstPos.y;
|
|
||||||
dstPos.y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPos.x + size.x > src.width) size.x = src.width - srcPos.x;
|
|
||||||
if (srcPos.y + size.y > src.height) size.y = src.height - srcPos.y;
|
|
||||||
if (dstPos.x + size.x > dst.width) size.x = dst.width - srcPos.x;
|
|
||||||
if (dstPos.y + size.y > dst.height) size.y = dst.height - srcPos.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ClipRect(Texture2D src, ref Vector2Int srcPos, Texture2D dst, ref Vector2Int dstPos, ref Vector2 srcSize, ref Vector2Int dstSize) {
|
|
||||||
var sizeRatio = dstSize / srcSize;
|
|
||||||
if (srcPos.x < 0) {
|
|
||||||
dstSize.x += (int) (srcPos.x * sizeRatio.x);
|
|
||||||
dstPos.x -= (int) (srcPos.x * sizeRatio.x);
|
|
||||||
srcSize.x += srcPos.x;
|
|
||||||
srcPos.x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPos.y < 0) {
|
|
||||||
dstSize.y += (int) (srcPos.y * sizeRatio.y);
|
|
||||||
dstPos.y -= (int) (srcPos.y * sizeRatio.y);
|
|
||||||
srcSize.y += srcPos.y;
|
|
||||||
srcPos.y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dstPos.x < 0) {
|
|
||||||
srcSize.x += dstPos.x / sizeRatio.x;
|
|
||||||
srcPos.x -= (int) (dstPos.x / sizeRatio.x);
|
|
||||||
dstSize.x += dstPos.x;
|
|
||||||
dstPos.x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dstPos.y < 0) {
|
|
||||||
srcSize.y += dstPos.y / sizeRatio.y;
|
|
||||||
srcPos.y -= (int) (dstPos.y / sizeRatio.y);
|
|
||||||
dstSize.y += dstPos.y;
|
|
||||||
dstPos.y = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPos.x + srcSize.x > src.width) {
|
|
||||||
srcSize.x = src.width - srcPos.x;
|
|
||||||
dstSize.x = (int) (srcSize.x * sizeRatio.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (srcPos.y + srcSize.y > src.height) {
|
|
||||||
srcSize.y = src.height - srcPos.y;
|
|
||||||
dstSize.y = (int) (srcSize.y * sizeRatio.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dstPos.x + dstSize.x > dst.width) {
|
|
||||||
dstSize.x = dst.width - srcPos.x;
|
|
||||||
srcSize.x = (int) (dstSize.x / sizeRatio.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dstPos.y + dstSize.y > dst.height) {
|
|
||||||
dstSize.y = dst.height - srcPos.y;
|
|
||||||
srcSize.y = (int) (dstSize.y / sizeRatio.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user