356 lines
12 KiB
C#
356 lines
12 KiB
C#
using UnityEngine;
|
||
using System.IO;
|
||
using System.Collections;
|
||
using System;
|
||
using Zenith;
|
||
|
||
public class TransparentCaptureSystem<T> : MonoBehaviour
|
||
{
|
||
[Header("相机设置")] public Camera targetCamera;
|
||
public KeyCode captureKey = KeyCode.C;
|
||
|
||
[Header("原始截图设置")] public int captureWidth = 1080;
|
||
public int captureHeight = 1920;
|
||
|
||
[Header("裁剪设置")] public int cropWidth = 1080;
|
||
public int cropHeight = 1920;
|
||
public Vector2 cropOffset = Vector2.zero; // 裁剪偏移量 (0-1)
|
||
|
||
[Header("透明通道")] public bool preserveAlpha = true;
|
||
public Color backgroundColor = new Color(0, 0, 0, 0); // 透明背景
|
||
|
||
[Header("输出设置")] public string saveFolder = "Screenshots";
|
||
public string fileNamePrefix = "Screenshot";
|
||
public bool saveAsPNG = true;
|
||
public string onlyFileName = "";
|
||
|
||
private RenderTexture _renderTexture;
|
||
private Texture2D _originalTexture;
|
||
private Texture2D _croppedTexture;
|
||
|
||
public Transform uiRoot;
|
||
public static T Instance;
|
||
|
||
private void Awake()
|
||
{
|
||
var allText = File.ReadAllText(Application.streamingAssetsPath + "/picSavePath.txt");
|
||
if (string.IsNullOrEmpty(allText))
|
||
{
|
||
#if !UNITY_EDITOR
|
||
saveFolder = Path.Combine(Application.dataPath, "Screenshots");
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
saveFolder = allText;
|
||
}
|
||
|
||
Init();
|
||
}
|
||
|
||
void Start()
|
||
{
|
||
if (targetCamera == null) targetCamera = Camera.main;
|
||
}
|
||
|
||
protected virtual void Init()
|
||
{
|
||
}
|
||
|
||
public IEnumerator CaptureAndCrop(Action<byte[]> callback = null)
|
||
{
|
||
// 确保尺寸有效
|
||
ValidateSizes();
|
||
|
||
// 创建原始截图
|
||
yield return StartCoroutine(CaptureOriginalScreenshot());
|
||
|
||
// 裁剪图片(保留透明通道)
|
||
CropWithTransparency();
|
||
|
||
// 保存结果
|
||
SaveCroppedImage();
|
||
|
||
// 清理资源
|
||
CleanupResources();
|
||
callback?.Invoke(bytes);
|
||
}
|
||
// 【核心修改】优化截图渲染流程,避免RT显示在屏幕上
|
||
private IEnumerator CaptureOriginalScreenshot()
|
||
{
|
||
// 1. 保存相机原始状态(增加关键状态保存)
|
||
RenderTexture originalRT = targetCamera.targetTexture;
|
||
CameraClearFlags originalClearFlags = targetCamera.clearFlags;
|
||
Color originalBackgroundColor = targetCamera.backgroundColor;
|
||
bool originalEnabled = targetCamera.enabled; // 保存相机启用状态
|
||
// bool originalRenderInEditMode = targetCamera.renderInEditMode; // 编辑模式渲染状态
|
||
|
||
// 2. 创建临时RenderTexture
|
||
_renderTexture = RenderTexture.GetTemporary(
|
||
captureWidth,
|
||
captureHeight,
|
||
32,
|
||
RenderTextureFormat.ARGB32,
|
||
RenderTextureReadWrite.Linear
|
||
);
|
||
|
||
try
|
||
{
|
||
// 3. 配置相机仅渲染到RT,不显示在屏幕
|
||
targetCamera.enabled = false; // 禁用相机的自动渲染
|
||
// targetCamera.renderInEditMode = true; // 允许编辑模式渲染
|
||
targetCamera.targetTexture = _renderTexture;
|
||
targetCamera.clearFlags = CameraClearFlags.SolidColor;
|
||
targetCamera.backgroundColor = preserveAlpha ? backgroundColor : Color.black;
|
||
|
||
// 4. 手动渲染相机到RT(此操作不会显示在屏幕)
|
||
targetCamera.Render();
|
||
|
||
// 5. 读取RT像素到Texture2D
|
||
RenderTexture.active = _renderTexture;
|
||
_originalTexture = new Texture2D(captureWidth, captureHeight,
|
||
preserveAlpha ? TextureFormat.ARGB32 : TextureFormat.RGB24, false);
|
||
_originalTexture.ReadPixels(new Rect(0, 0, captureWidth, captureHeight), 0, 0);
|
||
_originalTexture.Apply();
|
||
}
|
||
finally
|
||
{
|
||
// 6. 无论是否出错,都强制恢复相机设置
|
||
targetCamera.targetTexture = originalRT;
|
||
targetCamera.clearFlags = originalClearFlags;
|
||
targetCamera.backgroundColor = originalBackgroundColor;
|
||
targetCamera.enabled = originalEnabled;
|
||
// targetCamera.renderInEditMode = originalRenderInEditMode;
|
||
RenderTexture.active = null; // 强制取消RT激活状态
|
||
}
|
||
|
||
yield return null;
|
||
}
|
||
private IEnumerator CaptureOriginalScreenshot1()
|
||
{
|
||
// 创建RenderTexture(带alpha通道)
|
||
_renderTexture = new RenderTexture(captureWidth, captureHeight, 32, RenderTextureFormat.ARGB32);
|
||
_renderTexture.Create();
|
||
|
||
// 保存原始相机设置
|
||
RenderTexture originalRT = targetCamera.targetTexture;
|
||
CameraClearFlags originalClearFlags = targetCamera.clearFlags;
|
||
Color originalBackgroundColor = targetCamera.backgroundColor;
|
||
|
||
// 配置相机透明背景
|
||
targetCamera.targetTexture = _renderTexture;
|
||
targetCamera.clearFlags = CameraClearFlags.SolidColor;
|
||
targetCamera.backgroundColor = preserveAlpha ? backgroundColor : Color.black;
|
||
|
||
// 渲染相机
|
||
targetCamera.Render();
|
||
|
||
// 创建Texture2D(带alpha通道)
|
||
_originalTexture = new Texture2D(captureWidth, captureHeight,
|
||
preserveAlpha ? TextureFormat.ARGB32 : TextureFormat.RGB24,
|
||
false);
|
||
|
||
// 读取像素(包括alpha)
|
||
RenderTexture.active = _renderTexture;
|
||
_originalTexture.ReadPixels(new Rect(0, 0, captureWidth, captureHeight), 0, 0);
|
||
_originalTexture.Apply();
|
||
RenderTexture.active = null;
|
||
|
||
// 恢复相机设置
|
||
targetCamera.targetTexture = originalRT;
|
||
targetCamera.clearFlags = originalClearFlags;
|
||
targetCamera.backgroundColor = originalBackgroundColor;
|
||
|
||
yield return null;
|
||
}
|
||
|
||
string fullPath;
|
||
public string FullPath => fullPath;
|
||
|
||
private void CropWithTransparency()
|
||
{
|
||
if (_originalTexture == null)
|
||
{
|
||
Debug.LogError("原始截图未创建!");
|
||
return;
|
||
}
|
||
|
||
// 计算裁剪起始位置
|
||
int startX = Mathf.FloorToInt((_originalTexture.width - cropWidth) * cropOffset.x);
|
||
int startY = Mathf.FloorToInt((_originalTexture.height - cropHeight) * cropOffset.y);
|
||
|
||
// 确保在有效范围内
|
||
startX = Mathf.Clamp(startX, 0, _originalTexture.width - cropWidth);
|
||
startY = Mathf.Clamp(startY, 0, _originalTexture.height - cropHeight);
|
||
|
||
// 创建裁剪后的纹理(带alpha通道)
|
||
_croppedTexture = new Texture2D(cropWidth, cropHeight,
|
||
preserveAlpha ? TextureFormat.ARGB32 : TextureFormat.RGB24,
|
||
false);
|
||
|
||
// 获取原始纹理的像素数据(包括alpha)
|
||
Color[] pixels = _originalTexture.GetPixels(startX, startY, cropWidth, cropHeight);
|
||
|
||
// 设置到裁剪纹理
|
||
_croppedTexture.SetPixels(pixels);
|
||
_croppedTexture.Apply();
|
||
}
|
||
|
||
// public static void SaveImage(Texture2D tex, string name)
|
||
// {
|
||
// byte[] bytes = tex.EncodeToPNG();
|
||
// var dirPath = "SaveImages/";
|
||
// if (!Directory.Exists(dirPath))
|
||
// {
|
||
// Directory.CreateDirectory(dirPath);
|
||
// }
|
||
// File.WriteAllBytes(dirPath + name + ".png", B83.Image.PNGTools.ChangePPI(bytes, 300, 300));
|
||
// }
|
||
private void SaveCroppedImage()
|
||
{
|
||
if (_croppedTexture == null)
|
||
{
|
||
Debug.LogError("裁剪后的纹理未创建!");
|
||
return;
|
||
}
|
||
|
||
// 创建保存目录
|
||
string folderPath = saveFolder;
|
||
if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath);
|
||
|
||
// 生成文件名
|
||
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||
string extension = saveAsPNG ? ".png" : ".jpg";
|
||
string fileName;
|
||
string dpiName;
|
||
if (string.IsNullOrEmpty(onlyFileName))
|
||
fileName = $"{fileNamePrefix}_{cropWidth}x{cropHeight}_{timestamp}{extension}";
|
||
else
|
||
fileName = $"{onlyFileName}{extension}";
|
||
dpiName = $"{fileNamePrefix}_{cropWidth}x{cropHeight}_{timestamp}_DPI_{extension}";
|
||
|
||
fullPath = Path.Combine(folderPath, fileName);
|
||
var dpiFullPath = Path.Combine(folderPath, dpiName);
|
||
|
||
// 编码保存
|
||
byte[] imageBytes;
|
||
|
||
if (saveAsPNG)
|
||
{
|
||
imageBytes = _croppedTexture.EncodeToPNG();
|
||
}
|
||
else
|
||
{
|
||
// JPG不支持透明通道,强制去除alpha
|
||
Texture2D jpgTexture =
|
||
new Texture2D(_croppedTexture.width, _croppedTexture.height, TextureFormat.RGB24, false);
|
||
jpgTexture.SetPixels(_croppedTexture.GetPixels());
|
||
jpgTexture.Apply();
|
||
imageBytes = jpgTexture.EncodeToJPG(90);
|
||
Destroy(jpgTexture);
|
||
}
|
||
|
||
// File.WriteAllBytes(fullPath, imageBytes);
|
||
|
||
ImageDpiProcessor.SaveTextureWithDpi(_croppedTexture, fullPath, 300);
|
||
Debug.Log($"save as: {fullPath}");
|
||
|
||
// 验证透明度
|
||
if (saveAsPNG && preserveAlpha)
|
||
{
|
||
// 检查左上角像素是否透明
|
||
Color topLeft = _croppedTexture.GetPixel(0, _croppedTexture.height - 1);
|
||
Debug.Log($"保存的图片左上角透明度: {topLeft.a}");
|
||
|
||
// 检查中心像素是否透明
|
||
Color center = _croppedTexture.GetPixel(_croppedTexture.width / 2, _croppedTexture.height / 2);
|
||
Debug.Log($"保存的图片中心透明度: {center.a}");
|
||
}
|
||
|
||
Debug.Log($"截图已保存至: {fullPath}");
|
||
bytes = File.ReadAllBytes(fullPath);
|
||
// NetworkUpload.instance.miniCooperData.items[0].preview1 = System.Convert.ToBase64String(bytes);
|
||
|
||
#if UNITY_EDITOR
|
||
UnityEditor.EditorUtility.RevealInFinder(fullPath);
|
||
#endif
|
||
}
|
||
|
||
byte[] bytes;
|
||
|
||
private void ValidateSizes()
|
||
{
|
||
// 确保原始尺寸有效
|
||
captureWidth = Mathf.Clamp(captureWidth, 32, SystemInfo.maxTextureSize);
|
||
captureHeight = Mathf.Clamp(captureHeight, 32, SystemInfo.maxTextureSize);
|
||
|
||
// 确保裁剪尺寸有效
|
||
cropWidth = Mathf.Clamp(cropWidth, 32, captureWidth);
|
||
cropHeight = Mathf.Clamp(cropHeight, 32, captureHeight);
|
||
|
||
// 确保偏移量在0-1范围
|
||
cropOffset.x = Mathf.Clamp01(cropOffset.x);
|
||
cropOffset.y = Mathf.Clamp01(cropOffset.y);
|
||
}
|
||
|
||
private void CleanupResources()
|
||
{
|
||
if (_renderTexture != null)
|
||
{
|
||
Destroy(_renderTexture);
|
||
_renderTexture = null;
|
||
}
|
||
|
||
if (_originalTexture != null)
|
||
{
|
||
Destroy(_originalTexture);
|
||
_originalTexture = null;
|
||
}
|
||
|
||
// 保留裁剪纹理用于预览或后续使用
|
||
// 如果不再需要,可以取消注释下面一行
|
||
Destroy(_croppedTexture);
|
||
}
|
||
|
||
[ContextMenu("立即截图并裁剪")]
|
||
public virtual void CaptureAndCropNow(Action<byte[]> callback = null)
|
||
{
|
||
StartCoroutine(CaptureAndCrop(callback));
|
||
}
|
||
|
||
void OnGUI()
|
||
{
|
||
// 显示原始截图预览
|
||
if (_originalTexture != null)
|
||
{
|
||
GUI.DrawTexture(new Rect(10, 10, 200, 200 * (float)captureHeight / captureWidth),
|
||
_originalTexture, ScaleMode.ScaleToFit);
|
||
GUI.Label(new Rect(10, 220, 200, 30), $"原始尺寸: {captureWidth}x{captureHeight}");
|
||
}
|
||
|
||
// 显示裁剪后截图预览
|
||
if (_croppedTexture != null)
|
||
{
|
||
GUI.DrawTexture(new Rect(Screen.width - 210, 10, 200, 200 * (float)cropHeight / cropWidth),
|
||
_croppedTexture, ScaleMode.ScaleToFit);
|
||
GUI.Label(new Rect(Screen.width - 210, 220, 200, 30), $"裁剪尺寸: {cropWidth}x{cropHeight}");
|
||
|
||
// 显示透明度信息
|
||
if (preserveAlpha)
|
||
{
|
||
GUI.Label(new Rect(Screen.width - 210, 250, 200, 30), "包含透明通道");
|
||
}
|
||
}
|
||
}
|
||
|
||
void OnDestroy()
|
||
{
|
||
CleanupResources();
|
||
|
||
if (_croppedTexture != null)
|
||
{
|
||
Destroy(_croppedTexture);
|
||
_croppedTexture = null;
|
||
}
|
||
}
|
||
} |