VirtualFramework/Assets/Draw/Scripts/ScreenShotPainter.cs

1240 lines
42 KiB
C#
Raw Normal View History

2025-02-21 17:26:17 +08:00
using QFramework;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
2025-02-24 17:03:44 +08:00
using UnityEngine.Rendering;
2025-02-21 17:26:17 +08:00
using UnityEngine.UI;
[RequireComponent(typeof(Camera))]
public class ScreenShotPainter : MonoBehaviour
{
public class LineSegment
{
2025-02-24 17:03:44 +08:00
public RenderTexture Texture { get; set; }
2025-02-21 17:26:17 +08:00
public Color Color { get; set; }
public float Width { get; set; }
public bool IsEraser { get; set; }
public LineSegment(Color color, float width, bool isEraser)
{
2025-02-24 17:03:44 +08:00
Texture = RenderTexture.GetTemporary(Screen.width, Screen.height, 24);
2025-02-21 17:26:17 +08:00
Color = color;
Width = width;
IsEraser = isEraser;
}
}
public static ScreenShotPainter instance;
#region private numbers
//[SerializeField]
private string _captureSavePath;
//private Canvas _paintCanvas;
//[SerializeField]
private Texture _defaultBrushRawImage;
/// <summary>
/// 涂鸦的RenderTexture图片
/// </summary>
2025-02-24 17:03:44 +08:00
private RenderTexture _currentRenderTexture;
2025-02-21 17:26:17 +08:00
/// <summary>
/// 涂鸦的RenderTexture图片
/// </summary>
2025-02-24 17:03:44 +08:00
//private RenderTexture _historyRenderTexture;
2025-02-21 17:26:17 +08:00
/// <summary>
/// 截图灰化背景图
/// </summary>
//[SerializeField]
private RawImage _bgRawImage;
//绘图shader&material
//[SerializeField]
private Shader _paintBrushShader;
private Material _paintBrushMat;
//清理renderTexture的shader&material
//[SerializeField]
private Shader _clearBrushShader;
private Material _clearBrushMat;
//[SerializeField]
private Shader _eraserBrushShader;
private Material _eraserBrushMat;
//笔刷的默认颜色
[SerializeField]
private Color _defaultColor = Color.black;
//涂鸦的画布
[SerializeField]
private RawImage _paintCanvasImg;
/// <summary>
/// 笔刷的大小
/// </summary>
[SerializeField]
private float _brushSize = 300f;
/// <summary>
/// 橡皮擦的大小
/// </summary>
[SerializeField]
private float _eraserSize = 300f;
/// <summary>
/// 笔刷间隔
/// </summary>
private float _brushLerpSize;
/// <summary>
/// 默认上一次点的位置
/// </summary>
private Vector2 _lastPoint;
/// <summary>
/// 截图框选左下角点坐标
/// </summary>
private Vector2 _leftDownConnerPoint;
/// <summary>
/// 截图框选右上角点坐标
/// </summary>
private Vector2 _rightUpConnerPoint;
/// <summary>
/// 截图框选左上角点坐标
/// </summary>
private Vector2 _leftUpConnerPoint;
/// <summary>
/// 截图框选右下角点坐标
/// </summary>
private Vector2 _rightDownConnerPoint;
//屏幕的宽高
private int _screenWidth;
private int _screenHeight;
#region
//框选截图起始点
private Vector2 _startPoint;
private Vector2 _endPoint;
//框的颜色
[SerializeField]
private Color _rectColor = Color.red;
private bool _haveCirmformRectStarPoint;
#endregion
/// <summary>
/// 组件开关
/// </summary>
private bool _enabled;
private bool _eraserFlag;
private CaptureType _defaultCaptureType;
/// <summary>
/// 是否已经选了区域
/// </summary>
private bool _haveRegion;
private bool _drawRegionRect;
/// <summary>
/// 截图标记位
/// </summary>
private bool _captureFlag;
/// <summary>
/// 是否要截取UI
/// </summary>
[SerializeField]
private bool _captureWithUI;
private Material _lineMaterial;
#region
/// <summary>
/// 画完区域后要做的事情其实就是处理UI面板给外部调用
/// </summary>
private FinishedRegionEvent _finishedRegionEvent = new FinishedRegionEvent();
/// <summary>
/// 取消选择区域后要做的事情其实就是处理UI面板给外部调用
/// </summary>
private CannelRegionEvent _cannelRegionEvent = new CannelRegionEvent();
private FinishedCaptureEvent _finishedCapture = new FinishedCaptureEvent();
private EscapeCaptureEvent _escapeCaptureEvent = new EscapeCaptureEvent();
private EnterCaptureModeEvent _enterCaptureModeEvent = new EnterCaptureModeEvent();
private EraserModeUpdateEvent _eraserModeUpdateEvent = new EraserModeUpdateEvent();
#endregion
#endregion
#region public properties
public FinishedRegionEvent FinishedRegionEvent
{
get { return _finishedRegionEvent; }
}
public CannelRegionEvent CannelRegionEvent
{
get { return _cannelRegionEvent; }
}
public FinishedCaptureEvent FinishedCapture
{
get { return _finishedCapture; }
}
public EscapeCaptureEvent EscapeCaptureEvent
{
get { return _escapeCaptureEvent; }
}
#endregion
private Stack<LineSegment> _lineStack = new Stack<LineSegment>();
private LineSegment _currentLine;
public enum Status
{
Pen,
Eraser,
Line,
2025-02-24 11:11:14 +08:00
Rect,
Circle,
2025-05-06 17:22:00 +08:00
Arrow,
2025-02-21 17:26:17 +08:00
}
public EnterCaptureModeEvent EnterCaptureModeEvent
{
get { return _enterCaptureModeEvent; }
}
public EraserModeUpdateEvent EraserModeUpdateEvent
{
get { return _eraserModeUpdateEvent; }
}
#region life circle
bool isInited = false;
Status status = Status.Pen;
2025-02-24 17:03:44 +08:00
private CommandBuffer _commandBuffer;
private float _lastPaintTime;
private float _paintInterval = 0.02f; // 每0.02秒绘制一次
2025-02-24 17:37:06 +08:00
Image circleImg;
Image rectImg;
2025-02-21 17:26:17 +08:00
void Awake()
{
instance = this;
}
2025-02-24 17:37:06 +08:00
public void Init(RawImage drawImg, RawImage bgRawImg, Image circleImg, Image rectImg)
2025-02-21 17:26:17 +08:00
{
if (isInited == false)
{
isInited = true;
2025-02-24 17:37:06 +08:00
this.circleImg = circleImg;
this.rectImg = rectImg;
2025-02-24 17:03:44 +08:00
_commandBuffer = new CommandBuffer();
_commandBuffer.name = "PaintCommandBuffer";
2025-02-21 17:26:17 +08:00
_bgRawImage = bgRawImg;
_paintCanvasImg = drawImg;
_captureWithUI = true;
_captureSavePath = Application.dataPath;
_defaultCaptureType = CaptureType.FreeRegion;
//_paintCanvasImg = GameObject.FindObjectOfType<Canvas>().transform.Find("paintCanvasImg").GetComponent<RawImage>();
_screenWidth = Screen.width;
_screenHeight = Screen.height;
_paintBrushShader = Resources.Load<Shader>("Shaders/PaintBrush");
_paintBrushMat = new Material(_paintBrushShader);
_clearBrushShader = Resources.Load<Shader>("Shaders/ClearBrush");
_clearBrushMat = new Material(_clearBrushShader);
_eraserBrushShader = Resources.Load<Shader>("Shaders/EraserBrush");
_eraserBrushMat = new Material(_eraserBrushShader);
_defaultBrushRawImage = Resources.Load<Texture>("brush-1");
//初始化刷子
_paintBrushMat.SetTexture("_BrushTex", _defaultBrushRawImage);
_paintBrushMat.SetColor("_Color", _defaultColor);
_brushSize = PaintingParams.BrushDefaultSize;
_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / _brushSize;
_paintBrushMat.SetFloat("_Size", _brushSize);
_paintBrushMat.SetFloat("_SizeY", _brushSize * (float)_screenHeight / (float)_screenWidth);
_eraserSize = PaintingParams.EraserDefaultSize;
_eraserBrushMat.SetTexture("_BrushTex", _defaultBrushRawImage);
_eraserBrushMat.SetColor("_Color", new Color(0f, 0f, 0f, 0f));
_eraserBrushMat.SetFloat("_Size", _eraserSize);
_eraserBrushMat.SetFloat("_SizeY", _eraserSize * (float)_screenHeight / (float)_screenWidth);
2025-02-24 17:03:44 +08:00
_currentRenderTexture = RenderTexture.GetTemporary(_screenWidth, _screenHeight, 24);
_paintCanvasImg.texture = _currentRenderTexture;
2025-02-21 17:26:17 +08:00
// 激活渲染纹理,确保它可以被写入
2025-02-24 17:03:44 +08:00
RenderTexture.active = _currentRenderTexture;
2025-02-21 17:26:17 +08:00
GL.Clear(true, true, Color.clear);
//给画布添加事件
var paintEventTrigger = _paintCanvasImg.GetOrAddComponent<EventTrigger>();
paintEventTrigger.triggers = new List<EventTrigger.Entry>();
EventTrigger.Entry dragEntry = new EventTrigger.Entry();
dragEntry.eventID = EventTriggerType.Drag;
dragEntry.callback.AddListener(PaintDragging);
EventTrigger.Entry endDragEntry = new EventTrigger.Entry();
endDragEntry.eventID = EventTriggerType.EndDrag;
endDragEntry.callback.AddListener(OnPaintEndDrag);
paintEventTrigger.triggers.Add(dragEntry);
paintEventTrigger.triggers.Add(endDragEntry);
EventTrigger.Entry beginDrawEntry = new EventTrigger.Entry();
beginDrawEntry.eventID = EventTriggerType.BeginDrag;
beginDrawEntry.callback.AddListener(OnPaintBeginDrag);
//paintEventTrigger.triggers.Add(dragEntry);
paintEventTrigger.triggers.Add(beginDrawEntry);
// 使用 Graphics.Blit 清除渲染纹理
2025-02-24 17:03:44 +08:00
Graphics.Blit(null, _currentRenderTexture, _clearBrushMat);
2025-02-21 17:26:17 +08:00
// 恢复默认的渲染纹理
RenderTexture.active = null;
}
}
void Update()
{
if (_enabled)
{
switch (_defaultCaptureType)
{
case CaptureType.FullScreen:
break;
case CaptureType.FreeRegion:
//已经选了区域,可以开始涂鸦了
if (_haveRegion)
{
//如果按下右键,表示重新选择区域,清除掉之前的记录
if (Input.GetMouseButtonUp(1))
{
_defaultCaptureType = CaptureType.FreeRegion;
_haveRegion = false;
_haveCirmformRectStarPoint = false;
_startPoint = Vector2.zero;
_endPoint = Vector2.zero;
_drawRegionRect = false;
_leftUpConnerPoint = Vector2.zero;
_leftDownConnerPoint = Vector2.zero;
_rightUpConnerPoint = Vector2.zero;
_rightDownConnerPoint = Vector2.zero;
2025-02-24 17:03:44 +08:00
_currentRenderTexture.Release();
2025-02-21 17:26:17 +08:00
_bgRawImage.material.SetVector("_Rect", new Vector4(0, 0, 0, 0));
_eraserFlag = false;
//Graphics.Blit(_blitRenderTexture, _blitRenderTexture, _clearBrushMat); //清除一下
CannelRegionEvent.Invoke();
return;
}
//涂鸦的侦听逻辑
}
//未选择区域,要先选区域
else
{
//switch (_defaultCaptureType)
//{
// case CaptureType.FullScreen:
// break;
// case CaptureType.FreeRegion:
// //如果没有点下选择起点
if (!_haveCirmformRectStarPoint)
{
if (Input.GetMouseButtonDown(0))
{
//超出屏幕外
if (Input.mousePosition.x < 0 || Input.mousePosition.x > Screen.width ||
Input.mousePosition.y < 0 || Input.mousePosition.y > Screen.height)
{
return;
}
_startPoint = Input.mousePosition;
_haveCirmformRectStarPoint = true;
}
}
//如果选下了起点,刷新鼠标位置
else
{
_drawRegionRect = true;
if (Input.GetMouseButton(0))
{
_endPoint = Input.mousePosition;
//限制坐标在屏幕内
_endPoint.x = Mathf.Clamp(_endPoint.x, 0f, Screen.width);
_endPoint.y = Mathf.Clamp(_endPoint.y, 0f, Screen.height);
//设置灰化区域
Vector4 rect = new Vector4(Mathf.Min(_startPoint.x, _endPoint.x) / Screen.width,
Mathf.Min(_startPoint.y, _endPoint.y) / Screen.height,
Mathf.Max(_startPoint.x, _endPoint.x) / Screen.width,
Mathf.Max(_startPoint.y, _endPoint.y) / Screen.height);
_bgRawImage.material.SetVector("_Rect", rect);
}
//鼠标抬起,截图框选区域确定
if (Input.GetMouseButtonUp(0))
{
_endPoint = Input.mousePosition;
//限制坐标在屏幕内
_endPoint.x = Mathf.Clamp(_endPoint.x, 0f, Screen.width);
_endPoint.y = Mathf.Clamp(_endPoint.y, 0f, Screen.height);
_haveRegion = true;
_leftUpConnerPoint = GetCaptureViewLeftUpConnerPoint();
_rightUpConnerPoint = GetCaptureViewRightUpConnerPoint();
_rightDownConnerPoint = GetCaptureViewRightDownConnerPoint();
_leftDownConnerPoint = GetCaptureViewLeftDownConnerPoint();
FinishedRegionEvent.Invoke();
}
}
// break;
// default:
// throw new ArgumentOutOfRangeException();
//}
}
////侦听截图事件
//if (Input.GetKeyUp(KeyCode.Space))
//{
// _CaptureFlag = true;
//}
//全屏截图
if (Input.GetKeyUp(KeyCode.F) && _defaultCaptureType == CaptureType.FreeRegion && !_haveRegion)
{
_defaultCaptureType = CaptureType.FullScreen;
_leftDownConnerPoint = Vector2.zero;
_leftUpConnerPoint = new Vector2(0, _screenHeight);
_rightUpConnerPoint = new Vector2(_screenWidth, _screenHeight);
_rightDownConnerPoint = new Vector2(_screenWidth, 0);
_drawRegionRect = true;
_haveCirmformRectStarPoint = true;
_haveRegion = true;
_bgRawImage.gameObject.SetActive(false);
_bgRawImage.material.SetVector("_Rect", new Vector4(0, 0, 1, 1));
//涂鸦选项UI初始化
FinishedRegionEvent.Invoke();
}
break;
default:
break;
}
_eraserModeUpdateEvent.Invoke(_eraserFlag);
//退出截图
if (Input.GetKeyUp(KeyCode.Escape))
{
ResetData();
_enabled = false;
_escapeCaptureEvent.Invoke();
return;
}
}
}
void OnPostRender()
{
if (!enabled)
return;
if (_drawRegionRect)
{
//如果材质球不存在
if (!_lineMaterial)
{
//实例一个材质球
Shader shader = Shader.Find("Hidden/Internal-Colored");
_lineMaterial = new Material(shader);
_lineMaterial.hideFlags = HideFlags.HideAndDontSave;
//设置参数
_lineMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
_lineMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
//设置参数
_lineMaterial.SetInt("_Cull", (int)UnityEngine.Rendering.CullMode.Off);
//设置参数
_lineMaterial.SetInt("_ZWrite", 0);
}
GL.PushMatrix();//保存摄像机变换矩阵,把投影视图矩阵和模型视图矩阵压入堆栈保
GL.LoadPixelMatrix();//设置用屏幕坐标绘图
_lineMaterial.SetPass(0);
GL.Begin(GL.LINES);
GL.Color(_rectColor);
switch (_defaultCaptureType)
{
case CaptureType.FullScreen:
GL.Vertex3(_leftUpConnerPoint.x, _leftUpConnerPoint.y, 0);
GL.Vertex3(_rightUpConnerPoint.x, _rightUpConnerPoint.y, 0);
GL.Vertex3(_rightUpConnerPoint.x, _rightUpConnerPoint.y, 0);
GL.Vertex3(_rightDownConnerPoint.x, _rightDownConnerPoint.y, 0);
GL.Vertex3(_rightDownConnerPoint.x, _rightDownConnerPoint.y, 0);
GL.Vertex3(_leftDownConnerPoint.x, _leftDownConnerPoint.y, 0);
GL.Vertex3(_leftDownConnerPoint.x, _leftDownConnerPoint.y, 0);
GL.Vertex3(_leftUpConnerPoint.x, _leftUpConnerPoint.y, 0);
break;
case CaptureType.FreeRegion:
GL.Vertex3(_startPoint.x, _startPoint.y, 0);
GL.Vertex3(_endPoint.x, _startPoint.y, 0);
GL.Vertex3(_endPoint.x, _startPoint.y, 0);
GL.Vertex3(_endPoint.x, _endPoint.y, 0);
GL.Vertex3(_endPoint.x, _endPoint.y, 0);
GL.Vertex3(_startPoint.x, _endPoint.y, 0);
GL.Vertex3(_startPoint.x, _endPoint.y, 0);
GL.Vertex3(_startPoint.x, _startPoint.y, 0);
break;
default:
throw new ArgumentOutOfRangeException();
}
GL.End();
GL.PopMatrix();//恢复摄像机投影矩阵
}
//截图
if (_captureFlag && !_captureWithUI && _haveRegion)
{
_drawRegionRect = false;
float width = _rightUpConnerPoint.x - _leftDownConnerPoint.x;
float height = _rightUpConnerPoint.y - _leftDownConnerPoint.y;
Rect rect = new Rect(_leftDownConnerPoint.x, Screen.height - _leftUpConnerPoint.y, width, height);
Texture2D tex1 = new Texture2D((int)width, (int)height, TextureFormat.RGB24, false);
//Read the pixels in the Rect starting at 0,0 and ending at the screen's width and height
tex1.ReadPixels(rect, 0, 0, false);
tex1.Apply();
//Check that the display field has been assigned in the Inspector
#if UNITY_EDITOR
byte[] tex1bytes = tex1.EncodeToPNG();
string tex1path = _captureSavePath + "/RenderTextureActive.png";
System.IO.File.WriteAllBytes(tex1path, tex1bytes);
Debug.Log(string.Format("截屏了一张照片: {0}", tex1path));
#endif
//Texture2D CombineTex = new Texture2D(_renderTex.width, _renderTex.height, TextureFormat.ARGB32, false);
RenderTexture prev = RenderTexture.active;
2025-02-24 17:03:44 +08:00
RenderTexture.active = _currentRenderTexture;
2025-02-21 17:26:17 +08:00
Texture2D BliTexture2D = new Texture2D((int)width, (int)height, TextureFormat.ARGB32, false);
BliTexture2D.ReadPixels(rect, 0, 0);
#if UNITY_EDITOR
byte[] bytes = BliTexture2D.EncodeToPNG();
string path = _captureSavePath + "/BlitTexture.png";
string filename = path;
System.IO.File.WriteAllBytes(filename, bytes);
Debug.Log(string.Format("截屏了一张照片: {0}", filename));
#endif
RenderTexture.active = null;
Texture2D combineTexture2D = MergeImage(tex1, BliTexture2D);
byte[] combineTexture2DbBytes = combineTexture2D.EncodeToPNG();
string str = DateTime.Now.ToString("hh_mm_ss");
string fileName = DateTime.Now.ToString("yyyy_MM_dd") + "_" + str + ".png";
string path2 = _captureSavePath + "/" + fileName;
System.IO.File.WriteAllBytes(path2, combineTexture2DbBytes);
Debug.Log(string.Format("截屏了一张照片: {0}", path2));
_captureFlag = false;
//重置数据
ResetData();
_finishedCapture.Invoke();
}
//UI也截取
if (_captureFlag && _captureWithUI && _haveRegion)
{
StartCoroutine(StartCapture());
}
}
#endregion
#region private function
#region
2025-03-14 14:18:59 +08:00
public void SetStatus(Status status)
{
this.status = status;
ChangeToEraser(this.status == Status.Eraser);
}
2025-05-06 16:37:47 +08:00
Vector2 lineStart = Vector2.zero;
2025-02-21 17:26:17 +08:00
private void OnPaintBeginDrag(BaseEventData arg0)
{
if (_enabled && _haveRegion)
{
switch (status)
{
case Status.Pen:
break;
case Status.Line:
2025-05-06 16:37:47 +08:00
if (_currentLine == null)
2025-02-21 17:26:17 +08:00
{
2025-05-06 16:37:47 +08:00
_currentLine = new LineSegment(_paintBrushMat.GetColor("_Color"), _brushSize, _eraserFlag);
Graphics.Blit(_currentRenderTexture, _currentLine.Texture);
2025-02-21 17:26:17 +08:00
}
2025-05-06 16:37:47 +08:00
lineStart = Input.mousePosition;
_lastPoint = lineStart;
2025-02-24 11:11:14 +08:00
break;
case Status.Circle:
2025-02-24 17:03:44 +08:00
_currentLine = new LineSegment(_paintBrushMat.GetColor("_Color"), _brushSize, _eraserFlag);
_lastPoint = Input.mousePosition;
2025-02-24 17:37:06 +08:00
circleImg.gameObject.SetActive(true);
// TODO: 这样会使UI位置有偏差 后续修改
circleImg.transform.position = Input.mousePosition;
2025-02-24 17:03:44 +08:00
break;
case Status.Rect:
2025-02-24 17:37:06 +08:00
_currentLine = new LineSegment(_paintBrushMat.GetColor("_Color"), _brushSize, _eraserFlag);
2025-05-06 15:05:56 +08:00
Graphics.Blit(_currentRenderTexture, _currentLine.Texture);
2025-02-24 17:37:06 +08:00
_lastPoint = Input.mousePosition;
2025-02-21 17:26:17 +08:00
break;
2025-05-06 17:22:00 +08:00
case Status.Arrow:
_currentLine = new LineSegment(_paintBrushMat.GetColor("_Color"), _brushSize, _eraserFlag);
Graphics.Blit(_currentRenderTexture, _currentLine.Texture);
lineStart = Input.mousePosition;
_lastPoint = lineStart;
break;
2025-02-21 17:26:17 +08:00
}
}
}
private void PaintDragging(BaseEventData data)
{
if (_enabled && _haveRegion)
{
2025-02-24 17:03:44 +08:00
if (Input.GetMouseButton(0) && Time.time - _lastPaintTime > _paintInterval)
2025-02-21 17:26:17 +08:00
{
2025-02-24 17:03:44 +08:00
_lastPaintTime = Time.time;
// 绘制逻辑
if (Input.GetMouseButton(0))
2025-02-24 11:11:14 +08:00
{
2025-02-24 17:03:44 +08:00
switch (status)
{
case Status.Pen:
case Status.Eraser:
if (_currentLine == null)
{
_currentLine = new LineSegment(_paintBrushMat.GetColor("_Color"), _brushSize, _eraserFlag);
}
LerpPaint(Input.mousePosition, _eraserFlag);
break;
2025-05-06 16:37:47 +08:00
case Status.Line:
if (_lastPoint != default && Vector2.Distance(lineStart, Input.mousePosition) > 2f)
{
Graphics.Blit(_currentLine.Texture, _currentRenderTexture);
_paintCanvasImg.texture = _currentRenderTexture;
_lastPoint = lineStart;
Paint(_eraserFlag, lineStart);
LerpPaint(Input.mousePosition, _eraserFlag);
}
break;
2025-02-24 17:03:44 +08:00
case Status.Rect:
2025-05-06 15:05:56 +08:00
Graphics.Blit(_currentLine.Texture, _currentRenderTexture);
_paintCanvasImg.texture = _currentRenderTexture;
RectFactory();
2025-02-24 17:37:06 +08:00
break;
case Status.Circle:
2025-05-09 14:58:12 +08:00
Vector2 last;
Vector2 mouse;
RectTransformUtility.ScreenPointToLocalPointInRectangle(circleImg.rectTransform.parent as RectTransform, _lastPoint, null, out last);
RectTransformUtility.ScreenPointToLocalPointInRectangle(circleImg.rectTransform.parent as RectTransform, Input.mousePosition, null, out mouse);
var dis = Vector2.Distance(last, mouse) * 2;
2025-02-24 17:37:06 +08:00
circleImg.rectTransform.sizeDelta = new Vector2(dis, dis);
2025-02-24 17:03:44 +08:00
break;
2025-05-06 17:22:00 +08:00
case Status.Arrow:
Graphics.Blit(_currentLine.Texture, _currentRenderTexture);
_paintCanvasImg.texture = _currentRenderTexture;
ArrowFactory();
break;
2025-02-24 17:37:06 +08:00
2025-02-24 17:03:44 +08:00
}
2025-02-24 11:11:14 +08:00
}
2025-02-21 17:26:17 +08:00
}
}
}
private void OnPaintEndDrag(BaseEventData data)
{
if (_enabled && _haveRegion)
{
switch (status)
{
case Status.Pen:
case Status.Eraser:
if (Input.GetMouseButtonUp(0))
2025-02-24 11:11:14 +08:00
{
FinishedRaw();
}
break;
2025-02-24 17:03:44 +08:00
2025-02-24 11:11:14 +08:00
case Status.Line:
if (_currentLine != null)
{
2025-02-24 17:03:44 +08:00
FinishedRaw();
2025-05-06 16:37:47 +08:00
lineStart = Vector2.zero;
2025-02-24 11:11:14 +08:00
}
break;
2025-02-24 17:03:44 +08:00
case Status.Rect:
2025-05-06 17:22:00 +08:00
if (_currentLine != null)
FinishedRaw();
2025-02-24 11:11:14 +08:00
break;
2025-02-24 17:03:44 +08:00
2025-02-24 11:11:14 +08:00
case Status.Circle:
2025-02-24 17:03:44 +08:00
if (_currentLine != null)
2025-02-24 11:11:14 +08:00
{
float radius = Vector2.Distance(_lastPoint, Input.mousePosition);
var points = GenerateCirclePoints(_lastPoint, radius);
_lastPoint = default;
2025-02-24 17:03:44 +08:00
2025-02-24 11:11:14 +08:00
foreach (var item in points)
{
LerpPaint(item, false);
}
LerpPaint(points[0], false);
FinishedRaw();
2025-02-24 17:03:44 +08:00
2025-02-24 17:37:06 +08:00
circleImg.gameObject.SetActive(false);
2025-02-24 11:11:14 +08:00
}
2025-02-21 17:26:17 +08:00
break;
2025-02-24 17:03:44 +08:00
2025-05-06 17:22:00 +08:00
case Status.Arrow:
if (_currentLine != null)
{
FinishedRaw();
lineStart = Vector2.zero;
}
break;
2025-02-21 17:26:17 +08:00
default:
break;
}
}
}
2025-03-14 14:18:59 +08:00
public void FinishedRaw()
{
if (_currentLine != null)
{
// 保存当前纹理到 LineSegment
RenderTexture.active = _currentLine.Texture;
Graphics.Blit(_currentRenderTexture, _currentLine.Texture);
RenderTexture.active = null;
_lineStack.Push(_currentLine);
_currentLine = null;
}
_lastPoint = Vector2.zero;
}
2025-02-24 11:11:14 +08:00
List<Vector2> GenerateCirclePoints(Vector2 center, float r, int numPoints = 36)
{
List<Vector2> points = new List<Vector2>();
for (int i = 0; i < numPoints; i++)
{
// 计算每个点的角度
float angle = 2 * Mathf.PI * i / numPoints;
// 根据三角函数计算点的坐标
float x = center.x + r * Mathf.Cos(angle);
float y = center.y + r * Mathf.Sin(angle);
points.Add(new Vector2(x, y));
}
return points;
}
2025-02-24 17:03:44 +08:00
public void RectFactory()
{
if (_lastPoint != default)
{
2025-05-06 15:05:56 +08:00
var rectPoints = GenerateRectanglePoints(_lastPoint, Input.mousePosition);
LerpPaint(rectPoints[0], false);
LerpPaint(rectPoints[1], false);
LerpPaint(rectPoints[2], false);
LerpPaint(rectPoints[3], false);
LerpPaint(rectPoints[0], false);
//FinishedRaw();
2025-02-24 17:03:44 +08:00
}
}
2025-05-06 17:22:00 +08:00
public void ArrowFactory()
{
if (lineStart != default)
{
2025-05-09 14:58:12 +08:00
var points = GenerateArrowPoints(lineStart, Input.mousePosition, 0.1f);
2025-05-06 17:22:00 +08:00
_lastPoint = lineStart;
Paint(_eraserFlag, lineStart);
LerpPaint(Input.mousePosition, _eraserFlag);
LerpPaint(points[1], false);
_lastPoint = Input.mousePosition;
LerpPaint(points[3], false);
}
}
2025-02-24 17:03:44 +08:00
public Vector2[] GenerateRectanglePoints(Vector2 start, Vector2 thirdPoint)
2025-02-24 11:11:14 +08:00
{
Vector2[] points = new Vector2[4];
points[0] = start;
2025-02-24 17:03:44 +08:00
// 原本的 end 点(这里用 thirdPoint 替代)作为数组的第三个元素
points[2] = thirdPoint;
2025-02-24 11:11:14 +08:00
2025-02-24 17:03:44 +08:00
if (start.x < thirdPoint.x)
2025-02-24 11:11:14 +08:00
{
2025-02-24 17:03:44 +08:00
if (start.y < thirdPoint.y)
2025-02-24 11:11:14 +08:00
{
// 从左下方到右上方
2025-02-24 17:03:44 +08:00
points[1] = new Vector2(thirdPoint.x, start.y);
points[3] = new Vector2(start.x, thirdPoint.y);
2025-02-24 11:11:14 +08:00
}
else
{
// 从左上方到右下方
2025-02-24 17:03:44 +08:00
points[1] = new Vector2(thirdPoint.x, start.y);
points[3] = new Vector2(start.x, thirdPoint.y);
2025-02-24 11:11:14 +08:00
}
}
else
{
2025-02-24 17:03:44 +08:00
if (start.y < thirdPoint.y)
2025-02-24 11:11:14 +08:00
{
// 从右下方到左上方
2025-02-24 17:03:44 +08:00
points[1] = new Vector2(start.x, thirdPoint.y);
points[3] = new Vector2(thirdPoint.x, start.y);
2025-02-24 11:11:14 +08:00
}
else
{
// 从右上方到左下方
2025-02-24 17:03:44 +08:00
points[1] = new Vector2(start.x, thirdPoint.y);
points[3] = new Vector2(thirdPoint.x, start.y);
2025-02-24 11:11:14 +08:00
}
}
return points;
}
2025-05-06 17:22:00 +08:00
public Vector2[] GenerateArrowPoints(Vector2 start, Vector2 end, float arrowWidth = 0.3f, float arrowHeadLength = 0.2f)
{
Vector2[] points = new Vector2[4];
// 计算方向向量
Vector2 direction = end - start;
float length = direction.magnitude;
Vector2 normalizedDir = direction.normalized;
// 计算箭头头部的基准点(箭头左右两侧点所在的位置)
Vector2 arrowBase = end - normalizedDir * length * arrowHeadLength;
// 计算垂直于方向的向量(用于确定箭头宽度)
Vector2 perpendicular = new Vector2(-normalizedDir.y, normalizedDir.x) * length * arrowWidth;
// 箭头四个点的位置
points[0] = start; // 箭尾
points[1] = arrowBase - perpendicular; // 箭头左侧点
points[2] = end; // 箭头顶点
points[3] = arrowBase + perpendicular; // 箭头右侧点
return points;
}
2025-03-14 14:18:59 +08:00
public void Clear()
2025-02-24 11:11:14 +08:00
{
2025-03-14 14:18:59 +08:00
// 重新分配 RenderTexture
_currentRenderTexture = RenderTexture.GetTemporary(_screenWidth, _screenHeight, 24);
2025-02-24 17:03:44 +08:00
2025-03-14 14:18:59 +08:00
// 更新画布的纹理
_paintCanvasImg.texture = _currentRenderTexture;
2025-02-24 11:11:14 +08:00
2025-03-14 14:18:59 +08:00
// 清空线条栈
_lineStack.Clear();
2025-02-24 17:03:44 +08:00
}
2025-02-24 11:11:14 +08:00
2025-05-06 15:05:56 +08:00
2025-02-21 17:26:17 +08:00
public void Undo()
{
if (_lineStack.Count > 0)
{
// 移除最后一条线
var line = _lineStack.Pop();
2025-02-24 17:03:44 +08:00
// 如果栈中还有剩余的线条,切换到上一个线条的纹理
if (_lineStack.Count > 0)
2025-02-21 17:26:17 +08:00
{
2025-02-24 17:03:44 +08:00
var previousLine = _lineStack.Peek();
2025-03-14 14:18:59 +08:00
//RenderTexture.active = _currentRenderTexture;
//Graphics.Blit(previousLine.Texture, _currentRenderTexture);
//RenderTexture.active = null;
_currentRenderTexture = previousLine.Texture;
2025-02-24 17:03:44 +08:00
_paintCanvasImg.texture = previousLine.Texture;
2025-03-14 14:18:59 +08:00
2025-02-24 17:03:44 +08:00
}
else
{
Clear();
2025-02-21 17:26:17 +08:00
}
}
}
2025-02-24 17:03:44 +08:00
2025-02-21 17:26:17 +08:00
/// <summary>
/// 绘画进行插值
/// </summary>
/// <param name="point"></param>
2025-02-24 17:03:44 +08:00
Vector2[] vector2s = new Vector2[10000];
2025-02-21 17:26:17 +08:00
private void LerpPaint(Vector2 point, bool isEraser)
{
2025-02-24 17:03:44 +08:00
Paint(isEraser, point);
2025-02-21 17:26:17 +08:00
if (_lastPoint == Vector2.zero)
{
_lastPoint = point;
return;
}
float dis = Vector2.Distance(point, _lastPoint);
if (dis > _brushLerpSize)
{
Vector2 dir = (point - _lastPoint).normalized;
2025-02-24 17:03:44 +08:00
int num = (int)(dis / (_brushLerpSize));
2025-02-21 17:26:17 +08:00
for (int i = 0; i < num; i++)
{
2025-02-24 17:03:44 +08:00
vector2s[i] = _lastPoint + dir * (i + 1) * _brushLerpSize;
Paint(isEraser, vector2s[i]);
2025-02-21 17:26:17 +08:00
}
}
_lastPoint = point;
}
// 画点方法
2025-02-24 17:03:44 +08:00
private void Paint(bool isEraser, Vector2 point)
2025-02-21 17:26:17 +08:00
{
2025-02-24 17:03:44 +08:00
Material material = null;
2025-02-21 17:26:17 +08:00
if (point.x < _leftDownConnerPoint.x || point.x > _rightUpConnerPoint.x || point.y < _leftDownConnerPoint.y || point.y > _rightUpConnerPoint.y)
return;
2025-02-24 17:03:44 +08:00
Vector2 uv = new Vector2(point.x / (float)_screenWidth, point.y / (float)_screenHeight);
2025-02-21 17:26:17 +08:00
2025-02-24 17:03:44 +08:00
if (isEraser)
2025-02-21 17:26:17 +08:00
{
_eraserBrushMat.SetVector("_UV", uv);
2025-02-24 17:03:44 +08:00
material = _eraserBrushMat;
2025-02-21 17:26:17 +08:00
}
else
{
_paintBrushMat.SetVector("_UV", uv);
2025-02-24 17:03:44 +08:00
material = _paintBrushMat;
2025-02-21 17:26:17 +08:00
}
2025-02-24 17:03:44 +08:00
_commandBuffer.Blit(_currentRenderTexture, _currentRenderTexture, material);
Graphics.ExecuteCommandBuffer(_commandBuffer);
_commandBuffer.Clear();
}
2025-02-21 17:26:17 +08:00
public Vector2 GetCaptureViewLeftDownConnerPoint()
{
Vector2 vec = new Vector2(Mathf.Min(_startPoint.x, _endPoint.x), Mathf.Min(_startPoint.y, _endPoint.y));
return vec;
}
public Vector2 GetCaptureViewRightUpConnerPoint()
{
Vector2 vec = new Vector2(Mathf.Max(_startPoint.x, _endPoint.x), Mathf.Max(_startPoint.y, _endPoint.y));
return vec;
}
public Vector2 GetCaptureViewLeftUpConnerPoint()
{
Vector2 vec = new Vector2(Mathf.Min(_startPoint.x, _endPoint.x), Mathf.Max(_startPoint.y, _endPoint.y));
return vec;
}
public Vector2 GetCaptureViewRightDownConnerPoint()
{
Vector2 vec = new Vector2(Mathf.Max(_startPoint.x, _endPoint.x), Mathf.Min(_startPoint.y, _endPoint.y));
return vec;
}
#endregion
/// <summary>
/// 合成图片
/// </summary>
private Texture2D MergeImage(Texture2D tex1, Texture2D tex2)
{
for (int i = 0; i < tex1.width; i++)
{
for (int j = 0; j < tex1.height; j++)
{
Color color1 = tex1.GetPixel(i, j);
Color color2 = tex2.GetPixel(i, j);
Color newColor = color2.a * color2 + (1.0f - color2.a) * color1;
newColor.a = 1f;
tex1.SetPixel(i, j, newColor);
}
}
return tex1;
}
IEnumerator StartCapture()
{
yield return new WaitForEndOfFrame();
float width = _rightUpConnerPoint.x - _leftDownConnerPoint.x;
float height = _rightUpConnerPoint.y - _leftDownConnerPoint.y;
Rect rect = new Rect(_leftDownConnerPoint.x, _leftDownConnerPoint.y, width, height); //坑爹啊 如果是开携程在WaitForEndOfFrame时候截图那么他的坐标系换了。。rect起始点要设置在左下角
Texture2D tex1 = new Texture2D((int)width, (int)height, TextureFormat.RGB24, false);
//Read the pixels in the Rect starting at 0,0 and ending at the screen's width and height
tex1.ReadPixels(rect, 0, 0, false);
tex1.Apply();
//Check that the display field has been assigned in the Inspector
byte[] tex1bytes = tex1.EncodeToPNG();
string str = DateTime.Now.ToString("hh_mm_ss");
string fileName = DateTime.Now.ToString("yyyy_MM_dd") + "_" + str + ".png";
string tex1path = _captureSavePath + "/" + fileName;
System.IO.File.WriteAllBytes(tex1path, tex1bytes);
Debug.Log(string.Format("截屏了一张照片(有UI): {0}", tex1path));
_captureFlag = false;
ResetData();
//推出截屏
EscapeCaptureEvent.Invoke();
}
/// <summary>
/// 截图完成后重置数据
/// </summary>
private void ResetData()
{
_defaultCaptureType = CaptureType.FreeRegion;
_drawRegionRect = false;
_haveRegion = false;
_haveCirmformRectStarPoint = false;
2025-02-24 17:03:44 +08:00
_currentRenderTexture.Release();
2025-02-21 17:26:17 +08:00
_startPoint = Vector2.zero;
_endPoint = Vector2.zero;
_leftUpConnerPoint = Vector2.zero;
_leftDownConnerPoint = Vector2.zero;
_rightDownConnerPoint = Vector2.zero;
_rightUpConnerPoint = Vector2.zero;
_lastPoint = Vector2.zero;
_bgRawImage.material.SetVector("_Rect", new Vector4(0, 0, 1, 1));
_bgRawImage.gameObject.SetActive(false);
_paintCanvasImg.gameObject.SetActive(false);
_eraserFlag = false;
_enabled = false;
}
#endregion
#region public function
/// <summary>
/// 进入截图模式
/// </summary>
public void SwitchOn(bool isFullScreen)
{
if (isFullScreen)
{
_defaultCaptureType = CaptureType.FullScreen;
_bgRawImage.gameObject.SetActive(false);
_bgRawImage.material.SetVector("_Rect", new Vector4(0, 0, 1, 1));
_leftDownConnerPoint = Vector2.zero;
_leftUpConnerPoint = new Vector2(0, _screenHeight);
_rightUpConnerPoint = new Vector2(_screenWidth, _screenHeight);
_rightDownConnerPoint = new Vector2(_screenWidth, 0);
_drawRegionRect = true;
_haveCirmformRectStarPoint = true;
_haveRegion = true;
_lastPoint = Vector2.zero;
//涂鸦选项UI初始化
FinishedRegionEvent.Invoke();
}
else
{
_defaultCaptureType = CaptureType.FreeRegion;
_bgRawImage.gameObject.SetActive(true);
_bgRawImage.material.SetVector("_Rect", new Vector4(0, 0, 0, 0));
}
_paintCanvasImg.gameObject.SetActive(true);
_enterCaptureModeEvent.Invoke();
_enabled = true;
}
public void SaveCapture()
{
_captureFlag = true;
}
public void SetPaintColor(Color color)
{
_paintBrushMat.SetColor("_Color", color);
}
public void SetPaintingSize(float size)
{
_paintBrushMat.SetFloat("_Size", size);
_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / size;
_eraserBrushMat.SetFloat("_Size", size);
_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / size;
}
public void SetPaintingSize(float sizeX, float sizeY)
{
_paintBrushMat.SetFloat("_Size", sizeX);
_paintBrushMat.SetFloat("_SizeY", sizeY);
if (sizeX > sizeY)
_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / sizeX;
if (sizeX <= sizeY)
_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / sizeY;
}
public void SetEraserSize(float sizeX, float sizeY)
{
if (sizeX > sizeY)
_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / sizeX;
if (sizeX <= sizeY)
_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / sizeY;
_eraserBrushMat.SetFloat("_Size", sizeX);
_eraserBrushMat.SetFloat("_SizeY", sizeY);
//_brushLerpSize = (_defaultBrushRawImage.width + _defaultBrushRawImage.height) / 2.0f / size;
}
public void ChangeToEraser(bool toEraser)
{
_eraserFlag = toEraser;
}
#endregion
}
public class FinishedRegionEvent : UnityEvent
{
}
public class CannelRegionEvent : UnityEvent
{
}
public class FinishedCaptureEvent : UnityEvent
{
}
public class EscapeCaptureEvent : UnityEvent
{
}
public class EnterCaptureModeEvent : UnityEvent
{
}
public class EraserModeUpdateEvent : UnityEvent<bool>
{
}
public enum CaptureType
{
/// <summary>
/// 自动全屏截屏
/// </summary>
FullScreen,
/// <summary>
/// 自由框选截屏
/// </summary>
FreeRegion
}
/// <summary>
/// 笔刷橡皮擦计算参数 ,计算公式,比如笔刷的是【缩放倍数=BrushSizeMaxValue +1-slider.value*BrushSizeFactor】,橡皮擦类似
/// </summary>
public static class PaintingParams
{
/// <summary>
/// 缩小因子,越大说明可调节范围越大
/// </summary>
public static float BrushSizeFactor = 300f;
/// <summary>
/// 笔刷图片最大缩小倍数(画布大小的笔刷图案要缩小多少倍数显示,缩小的越小,笔刷显示效果越大)
/// </summary>
public static float BrushSizeMaxValue = 50;
public static float EraserSizeFactor = 50;
public static float EraserSizeMaxValue = 50;
public static float BrushDefaultSize = 300;
public static float EraserDefaultSize = 100;
}