1. 程式人生 > >Unity 程式碼實現PS的功能和區域性截圖及合併分享圖片

Unity 程式碼實現PS的功能和區域性截圖及合併分享圖片

一、功能描述:
做一個獲取遊戲內容分享圖片功能,分截圖和長圖分享。圖片上要顯示玩家自定義的頭像 以及名字。

二、實現思路:
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);