Unity 程式碼實現PS的功能和區域性截圖及合併分享圖片
阿新 • • 發佈:2019-02-06
一、功能描述:
做一個獲取遊戲內容分享圖片功能,分截圖和長圖分享。圖片上要顯示玩家自定義的頭像 以及名字。
二、實現思路:
1、分享直接使用的share SDK 即可。
2、從伺服器下載背景模板圖片。
3、將玩家名字顯示截圖 生成圖片。
4、下載讀取頭像圖片
5、將名字圖片和頭像圖示分別合併到背景模板圖片。
6、儲存圖片到本地
三、程式碼實現:
1、下載圖片程式碼
下載相關參考了一個大神的程式碼做了修飾。原地址點選檢視
using System;
using System.IO;
using UnityEngine;
public abstract class DownloadItem {
/// <summary>
/// 網路資源url路徑
/// </summary>
protected string m_srcUrl;
/// <summary>
/// 資源下載存放路徑,不包含檔名
/// </summary>
protected string m_savePath;
/// <summary>
/// 檔名,不包含字尾
/// </summary>
protected string m_fileNameWithoutExt;
/// <summary>
/// 檔案字尾
/// </summary>
protected string m_fileExt;
/// <summary>
/// 下載檔案全路徑,路徑+檔名+字尾
/// </summary>
protected string m_saveFilePath;
/// <summary>
/// 原檔案大小
/// </summary>
protected long m_fileLength;
/// <summary>
/// 當前下載好了的大小
/// </summary>
protected long m_currentLength;
/// <summary>
/// 是否開始下載
/// </summary>
protected bool m_isStartDownload;
public bool isStartDownload {
get {
return m_isStartDownload;
}
}
public DownloadItem(string url, string path) {
m_srcUrl = url;
m_savePath = path;
m_isStartDownload = false;
m_fileNameWithoutExt = Path.GetFileNameWithoutExtension(m_srcUrl);
m_fileExt = Path.GetExtension(m_srcUrl);
m_saveFilePath = m_savePath;//string.Format("{0}/{1}{2}", m_savePath, m_fileNameWithoutExt, m_fileExt);
}
/// <summary>
/// 開始下載
/// </summary>
/// <param name="callback">下載完成回撥</param>
public virtual void StartDownload(MonoBehaviour behaviour,Action failedDelegate, Action completedDelegate = null,bool isImage = false) {
if (string.IsNullOrEmpty(m_srcUrl) || string.IsNullOrEmpty(m_savePath)) {
return;
}
if (behaviour == null)
return;
//若存放目錄不存在則建立目錄
//if (!string.IsNullOrEmpty(m_saveFilePath)) {
// string dirName = Path.GetDirectoryName(m_saveFilePath);
// if (!Directory.Exists(dirName)) {
// Directory.CreateDirectory(dirName);
// }
//}
}
/// <summary>
/// 獲取下載進度
/// </summary>
/// <returns>進度,0-1</returns>
public abstract float GetProcess();
/// <summary>
/// 獲取當前下載了的檔案大小
/// </summary>
/// <returns>當前檔案大小</returns>
public abstract long GetCurrentLength();
/// <summary>
/// 獲取要下載的檔案大小
/// </summary>
/// <returns>檔案大小</returns>
public abstract long GetLength();
public abstract void Destroy();
}
using System;
using System.Collections;
using System.IO;
using System.Net;
using UnityEngine;
/// <summary>
/// HTTP的方式下載,支援斷點續傳
/// </summary>
public class HttpDownloadItem : DownloadItem {
/// <summary>
/// 臨時檔案字尾名
/// </summary>
string m_tempFileExt = ".temp";
/// <summary>
/// 臨時檔案全路徑
/// </summary>
string m_tempSaveFilePath;
public HttpDownloadItem(string url, string path) : base(url, path) {
m_tempSaveFilePath = string.Format("{0}/{1}{2}", m_savePath, m_fileNameWithoutExt, m_tempFileExt);
}
public override void StartDownload(MonoBehaviour behaviour,Action failedDelegate, Action completedDelegate = null,bool isImage = false) {
base.StartDownload(behaviour,failedDelegate,completedDelegate,isImage);
behaviour.StartCoroutine(Download(failedDelegate,completedDelegate));
}
IEnumerator Download(Action failedDelegate, Action completedDelegate = null) {
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_srcUrl);
request.Method = "GET";
FileStream fileStream;
if (File.Exists(m_tempSaveFilePath)) {
//若之前已下載了一部分,繼續下載
fileStream = File.OpenWrite(m_tempSaveFilePath);
m_currentLength = fileStream.Length;
fileStream.Seek(m_currentLength, SeekOrigin.Current);
//設定下載的檔案讀取的起始位置
request.AddRange((int)m_currentLength);
} else {
//第一次下載
fileStream = new FileStream(m_tempSaveFilePath, FileMode.Create, FileAccess.Write);
m_currentLength = 0;
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream = response.GetResponseStream();
//總的檔案大小=當前需要下載的+已下載的
m_fileLength = response.ContentLength + m_currentLength;
m_isStartDownload = true;
int lengthOnce;
int bufferMaxLength = 1024 * 20;
while (m_currentLength < m_fileLength) {
byte[] buffer = new byte[bufferMaxLength];
if (stream.CanRead) {
//讀寫操作
lengthOnce = stream.Read(buffer, 0, buffer.Length);
m_currentLength += lengthOnce;
fileStream.Write(buffer, 0, lengthOnce);
} else {
break;
}
yield return null;
}
m_isStartDownload = false;
response.Close();
stream.Close();
fileStream.Close();
//臨時檔案轉為最終的下載檔案
File.Move(m_tempSaveFilePath, m_saveFilePath);
if (completedDelegate != null) {
completedDelegate();
}
}
public override float GetProcess() {
if (m_fileLength > 0) {
return Mathf.Clamp((float)m_currentLength / m_fileLength, 0, 1);
}
return 0;
}
public override long GetCurrentLength() {
return m_currentLength;
}
public override long GetLength() {
return m_fileLength;
}
public override void Destroy() {
}
}
using System;
using System.Collections;
using System.IO;
using UnityEngine;
/// <summary>
/// WWW的方式下載
/// </summary>
public class WWWDownloadItem : DownloadItem {
WWW m_www;
public WWWDownloadItem(string url, string path) : base(url, path) {
}
public override void StartDownload(MonoBehaviour behaviour, Action failedDelegate, Action completedDelegate = null,bool isImage = false) {
base.StartDownload(behaviour,failedDelegate,completedDelegate,isImage);
if(isImage)
behaviour.StartCoroutine(LoadFromUrl(failedDelegate, completedDelegate));
else
behaviour.StartCoroutine(Download(failedDelegate, completedDelegate));
}
IEnumerator Download(Action failedDelegate, Action completedDelegate = null) {
m_www = new WWW(m_srcUrl);
m_isStartDownload = true;
yield return m_www;
//WWW讀取完成後,才開始往下執行
m_isStartDownload = false;
if (m_www.isDone) {
byte[] bytes = m_www.bytes;
//建立檔案
FileInfo file = new FileInfo(m_saveFilePath);
Stream stream = file.Create();
stream.Write(bytes, 0, bytes.Length);
stream.Close();
stream.Dispose();
} else {
Debug.Log("Download Error:" + m_www.error);
if (failedDelegate != null) {
failedDelegate();
}
}
if (completedDelegate != null) {
completedDelegate();
}
}
private IEnumerator LoadFromUrl(Action failedDelegate, System.Action completedDelegate = null) {
m_www = new WWW(m_srcUrl);
m_isStartDownload = true;
yield return m_www;
try {
if (!string.IsNullOrEmpty(m_www.error)) {
if (failedDelegate != null) {
failedDelegate();
}
Client.LogManager.LogError(m_www.error);
} else if (m_www.isDone) {
Texture2D texture = m_www.texture;
texture.filterMode = FilterMode.Bilinear;
byte[] pngData = texture.EncodeToJPG();
FileUtils.WriteAllBytes(PathUtils.CombinePath(PathUtils.TempPath, m_srcUrl.GetHashCode().ToString()), pngData);
if (completedDelegate != null)
completedDelegate();
}
} catch (System.Exception) {
m_www = null;
yield break;
}
m_www = null;
yield return null;
}
public override float GetProcess() {
if (m_www != null) {
return m_www.progress;
}
return 0;
}
public override long GetCurrentLength() {
if (m_www != null) {
return m_www.bytesDownloaded;
}
return 0;
}
public override long GetLength() {
return 0;
}
public override void Destroy() {
if (m_www != null) {
m_www.Dispose();
m_www = null;
}
}
}
2、文字生成圖片
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using Client;
using System.IO;
using System;
public class TextToTexture : MonoBehaviour {
const string PREFAB_NAME = "TextToTexture";
const string SAVE_FILE_NAME = "temp_text_to_texture.png";
private static TextToTexture mCurrent;
private Text mText;
private Image mImage;
private bool mIsWorking = false;
private Action<string> onCreateFinish;
public static void Create(int fontSize, string target, Action<string> finishCallback = null) {
CreateTarget();
mCurrent.CreateTextTexture(fontSize, target, finishCallback);
}
private static void CreateTarget() {
if (mCurrent == null) {
GameObject go = RPrefab.Singleton.Instantiate(RPrefab.kPrefabShareText,PREFAB_NAME,WindowRoot.s_Current.windowTop);
//go.transform.SetParent(WindowRoot.s_Current.windowTop, false);
go.hideFlags = HideFlags.HideAndDontSave;
mCurrent = go.AddComponent<TextToTexture>();
}
else {
if (mCurrent.mIsWorking) {
Client.LogManager.LogError("the class is working");
return;
}
mCurrent.transform.SetAsLastSibling();
}
}
void Awake() {
mText = GetComponentInChildren<Text>();
mImage = GetComponentInChildren<Image>();
}
void OnDestroy() {
if (mCurrent != null) {
Destroy(mCurrent);
}
}
private void CreateTextTexture(int fontSize, string target, Action<string> finishCallback) {
mIsWorking = true;
onCreateFinish = finishCallback;
mCurrent.gameObject.SetActive(true);
float factor = ResolutionUtils.scaleFactor;
mText.fontSize = (int)(fontSize / factor);
mText.text = target;
//mImage.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, mText.preferredWidth);
//mImage.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, mText.preferredHeight);
StartCoroutine(SyncSaveTextTexture());
}
private IEnumerator SyncSaveTextTexture() {
yield return 3;
yield return new WaitForEndOfFrame();
float factor = ResolutionUtils.scaleFactor;
//Debug.Log("factor = " + factor + " " + mText.preferredWidth);
int width = (int)(mText.preferredWidth * factor);
//Debug.Log("factor width = " + width);
int height = (int)(mText.preferredHeight * factor);
RectTransform rect = mText.transform.GetComponent<RectTransform>();
width = (int)(rect.rect.width * factor);
//int height = (int)rect.rect.height;
//Debug.Log("rect width = " + width);
Texture2D target = new Texture2D(width, height, TextureFormat.ARGB32, false);
target.ReadPixels(new Rect(0, 0, width, height), 0, 0);
target = PsTexture(target);
string path = PathUtils.CombinePath( PathUtils.TempPath,SAVE_FILE_NAME);
string filePath = PathUtils.CombinePath(PathUtils.TempPath, path.GetHashCode().ToString());
FileUtils.WriteAllBytes(filePath, target.EncodeToPNG());
mCurrent.gameObject.SetActive(false);
if (onCreateFinish != null) {
onCreateFinish.Invoke(path);
onCreateFinish = null;
}
mIsWorking = false;
}
/// <summary>
/// 加工Texture2D圖片
/// </summary>
/// <param name="source">原圖片資料</param>
/// <param name="scale">縮放圖片</param>
/// <param name="filterColor">圖片顏色加工,顏色值相加</param>
/// <returns>加工好的圖片</returns>
Texture2D PsTexture(Texture2D source, float scale = 1, Color filterColor = default(Color)) {
Texture2D result = new Texture2D((int)(source.width * scale), (int)(source.height * scale), source.format, false);//圖片縮放生成新的圖片
for (int i = 0; i < source.height; ++i) {
for (int j = 0; j < source.width; ++j) {
Color color = source.GetPixelBilinear((float)j / (float)source.width, (float)i / (float)source.height);
Color color1 = source.GetPixel(1,1);
//color -= color1;//對截圖進行濾鏡處理
if(color1 == color)
color.a = 0.0f;
result.SetPixel((int)(j * scale), (int)(i * scale), color);//按照縮放比例寫入圖片
}
}
result.Apply();
return result;
}
}
3、縮放圖片大小
using System.Threading;
using UnityEngine;
namespace Client {
public class TextureScale {
public class ThreadData {
public int start;
public int end;
public ThreadData(int s, int e) {
start = s;
end = e;
}
}
private static Color[] texColors;
private static Color[] newColors;
private static int w;
private static float ratioX;
private static float ratioY;
private static int w2;
private static int finishCount;
private static Mutex mutex;
public static void Point(Texture2D tex, int newWidth, int newHeight) {
ThreadedScale(tex, newWidth, newHeight, false);
}
public static void Bilinear(Texture2D tex, int newWidth, int newHeight) {
ThreadedScale(tex, newWidth, newHeight, true);
}
private static void ThreadedScale(Texture2D tex, int newWidth, int newHeight, bool useBilinear) {
texColors = tex.GetPixels();
newColors = new Color[newWidth * newHeight];
if (useBilinear) {
ratioX = 1.0f / ((float)newWidth / (tex.width - 1));
ratioY = 1.0f / ((float)newHeight / (tex.height - 1));
}
else {
ratioX = ((float)tex.width) / newWidth;
ratioY = ((float)tex.height) / newHeight;
}
w = tex.width;
w2 = newWidth;
var cores = Mathf.Min(SystemInfo.processorCount, newHeight);
var slice = newHeight / cores;
finishCount = 0;
if (mutex == null) {
mutex = new Mutex(false);
}
if (cores > 1) {
int i = 0;
ThreadData threadData;
for (i = 0; i < cores - 1; i++) {
threadData = new ThreadData(slice * i, slice * (i + 1));
ParameterizedThreadStart ts = useBilinear ? new ParameterizedThreadStart(BilinearScale) : new ParameterizedThreadStart(PointScale);
Thread thread = new Thread(ts);
thread.Start(threadData);
}
threadData = new ThreadData(slice * i, newHeight);
if (useBilinear) {
BilinearScale(threadData);
}
else {
PointScale(threadData);
}
while (finishCount < cores) {
Thread.Sleep(1);
}
}
else {
ThreadData threadData = new ThreadData(0, newHeight);
if (useBilinear) {
BilinearScale(threadData);
}
else {
PointScale(threadData);
}
}
tex.Resize(newWidth, newHeight);
tex.SetPixels(newColors);
tex.Apply();
}
public static void BilinearScale(System.Object obj) {
ThreadData threadData = (ThreadData)obj;
for (var y = threadData.start; y < threadData.end; y++) {
int yFloor = (int)Mathf.Floor(y * ratioY);
var y1 = yFloor * w;
var y2 = (yFloor + 1) * w;
var yw = y * w2;
for (var x = 0; x < w2; x++) {
int xFloor = (int)Mathf.Floor(x * ratioX);
var xLerp = x * ratioX - xFloor;
newColors[yw + x] = ColorLerpUnclamped(ColorLerpUnclamped(texColors[y1 + xFloor], texColors[y1 + xFloor + 1], xLerp),
ColorLerpUnclamped(texColors[y2 + xFloor], texColors[y2 + xFloor + 1], xLerp),
y * ratioY - yFloor);
}
}
mutex.WaitOne();
finishCount++;
mutex.ReleaseMutex();
}
public static void PointScale(System.Object obj) {
ThreadData threadData = (ThreadData)obj;
for (var y = threadData.start; y < threadData.end; y++) {
var thisY = (int)(ratioY * y) * w;
var yw = y * w2;
for (var x = 0; x < w2; x++) {
newColors[yw + x] = texColors[(int)(thisY + ratioX * x)];
}
}
mutex.WaitOne();
finishCount++;
mutex.ReleaseMutex();
}
private static Color ColorLerpUnclamped(Color c1, Color c2, float value) {
return new Color(c1.r + (c2.r - c1.r) * value,
c1.g + (c2.g - c1.g) * value,
c1.b + (c2.b - c1.b) * value,
c1.a + (c2.a - c1.a) * value);
}
}
}
4、合併圖片
using UnityEngine;
using System.Collections;
namespace Client {
public static class TextureUtils {
/// <summary>
/// 圖片合併
/// </summary>
/// <param name="background">背景圖</param>
/// <param name="front">前景圖</param>
/// <param name="startX">開始x座標</param>
/// <param name="startY">開始y座標</param>
/// <returns>返回合併後的背景圖</returns>
public static Texture2D Combine(Texture2D background, Texture2D front, int startX, int startY) {
for (int x = startX; x < background.width; x++) {
if (x < 0) {
x = 0;
}
if (x > startX + front.width) {
break;
}
for (int y = startY; y < background.height; y++) {
if (y < 0) {
y = 0;
}
if (y > startY + front.height) {
break;
}
Color bgColor = background.GetPixel(x, y);
Color frColor = front.GetPixel(x - startX, y - startY);
Color finalColor = Color.Lerp(bgColor, frColor, frColor.a / 1.0f);
background.SetPixel(x, y, finalColor);
}
}
background.Apply();
return background;
}
/// <summary>
/// 圖片合併
/// </summary>
/// <param name="background">背景圖</param>
/// <param name="front">前景圖</param>
/// <param name="startX">開始x座標</param>
/// <param name="startY">開始y座標</param>
/// <returns>返回合併後的背景圖</returns>
public static IEnumerator SyncCombine(Texture2D background, Texture2D front, int startX, int startY, int frameLoopCount) {
int loopCount = 0;
for (int x = startX; x < background.width; x++) {
if (x < 0) {
x = 0;
}
if (x > startX + front.width) {
break;
}
for (int y = startY; y < background.height; y++) {
if (y < 0) {
y = 0;
}
if (y > startY + front.height) {
break;
}
if (loopCount >= frameLoopCount) {
loopCount = 0;
yield return 1;
}
loopCount++;
Color bgColor = background.GetPixel(x, y);
Color frColor = front.GetPixel(x - startX, y - startY);
Color finalColor = Color.Lerp(bgColor, frColor, frColor.a / 1.0f);
background.SetPixel(x, y, finalColor);
}
}
background.Apply();
}
public static Vector2 GetOpacitySize(this Texture2D texture, float minAlpha = 0) {
int minx = 0, maxx = 0, miny = 0, maxy = 0;
bool find = false;
for (int i = 0; i < texture.height; i++) {
for (int j = 0; j < texture.width; j++) {
if (texture.GetPixel(j, i).a > minAlpha) {
miny = i;
find = true;
break;
}
}
if (find) {
break;
}
}
find = false;
for (int i = texture.height; i >= 0; i--) {
for (int j = texture.width; j >= 0; j--) {
if (texture.GetPixel(j, i).a > minAlpha) {
maxy = i;
find = true;
break;
}
}
if (find) {
break;
}
}
find = false;
for (int i = 0; i < texture.width; i++) {
for (int j = miny; j < maxy; j++) {
if (texture.GetPixel(i, j).a > minAlpha) {
minx = i;
find = true;
break;
}
}
if (find) {
break;
}
}
find = false;
for (int i = texture.width; i >= 0; i--) {
for (int j = maxy; j >= miny; j--) {
if (texture.GetPixel(i, j).a > minAlpha) {
maxx = i;
find = true;
break;
}
}
if (find) {
break;
}
}
return new Vector2(maxx - minx, maxy - miny);
}
}
}
5、呼叫例子
TextureScale.Bilinear(headIconImage.sprite.texture, 128, 130);
TextureUtils.Combine(headIconImage.sprite.texture, headIconBGImage.sprite.texture, 0, 0);
Texture2D texture = TextureUtils.Combine(mainBgImage.sprite.texture, headIconImage.sprite.texture, 21, 28);
//mainBgImage.sprite = Sprite.Create(texture, new Rect(0, 0, 64,
//64), new Vector2(0, 0));
TextToTexture.Create(26, string.Format("<color=#F86F7DFF>{0}</color>", setting.playerData.InviteCode), activeAddText);