【WPF】 InkCanvas 書寫毛筆效果


首先貼出本文參考學習的文章吧。

https://www.cnblogs.com/LCHL/p/9055642.html#4206298

感謝這位懶羊羊博主的代碼和講解,我在此基礎上稍微加了一些東西,希望能使書寫效果得到更好的提升吧。建議先從羊博主的博文看起。


前文

  • 本文致力於解決羊博主的2-3問題,即代碼粗細度的優化。
  • 本文本着分享知識的目的寫出,希望能對一些尋找類似知識的人一些幫助,能得到各位的批評指正也是榮幸萬分。
  • 因工作公司的原因,無法貼出全部代碼,我盡量在下文中對自己的思路進行詳細的講解。(要吃飯的呀)

關鍵詞

Freeze, DrawingContext


思路

1. 卡頓問題

羊博主提出使用WPF中的Freeze()方法將畫刷“凍結”。

Freezable 類提供特殊功能,以便在使用修改或復制開銷很大的對象時幫助提高應用程序性能。

筆者使用該方法后,書寫性能得到了很大提升,但是書寫點數目達到萬為單位時,出現書寫不流暢的情況,且越書寫越卡頓。筆者冥思苦想三天三夜,嘗試了很多方法都沒有得到解決。果然皇天不負有心人,終於還是讓我找到了一些改進方法。代碼如下:

ImageDrawing image = new ImageDrawing()
{
    ImageSource = imageSource,
    Rect = new Rect(x - t1 / 2.0, y - t1 / 2.0, t1, t1)
    //t1 為當前的粗細度;x為當前的X坐標,y為當前Y坐標
};
    image.Freeze();
    drawingContext.DrawDrawing(image);

DrawImage更改為DrawDrawing,書寫性能確實得到了雖然微弱但可見的提升。(自己給自己鼓掌)但是具體有多少提升,筆者並沒有做量化。(太懶了) 效果基本滿意,如果您有更好的解決方法,希望能得到您的指點。

后文,筆者在羊博主的指點下讓書寫效率又得到了數以十倍的提升,非常感謝羊兄的指導。然筆者愚鈍,力有不逮,不能向讀者說出其精髓之一二,實在抱歉。該優化方法在羊博主文章中也部分寫出,對於這方面有要求的讀者,可以學習羊博主的博客。

2. 顏色問題

羊博主博客已完美解決該問題,棒棒噠。✿✿ヽ(°▽°)ノ✿)

3. 粗細問題

  • 粗細度問題可以分解為兩個問題,一個是書寫時間,一個是書寫距離。簡而言之就是書寫速度越快,筆跡越細;書寫速度越慢,筆跡越粗。(速度v = 距離s / 時間t,這個大家應該都知道)

3.1 書寫距離

書寫距離應該怎么獲得?

--- 無非就是兩個點之間的距離。

  1. 在InkCanvas下重寫OnDraw方法(詳見羊博主博文
  2. 在OnDraw方法中帶了參數StylusPointCollection,該參數為書寫點的搜集。該參數搜集動態繪制時產生的點,一般為兩個點,即繪制一條線段時的前后兩個點,這兩個點相減就得到了兩點間的距離。

3.2 書寫時間

這個參數在Ondraw中沒有,如果想關聯該參數的話可以研究一下RawStylusInput.Timestamp,(項目組的大神把這些都搭建好了,我用就完事了)該參數可以獲取發生輸入的時間,兩點時間相減就獲得了間距的書寫時間。兩點就是兩條線段的終點,因為在繪制時是連續的線,所以后面的線的起點和前面線的終點為相同的點,這個應該很好理解。

3.3 書寫速度

通過距離除以時間得出書寫速度,自己規定一個速度閾值,在大於這個閾值時,將粗細值縮小;小於這個閾值時,將粗細度放大。該閾值就看你自己想要書寫的效果定。建議將進行加減時的值設置為定值,這樣線條變化更加平滑。

以下代碼不包含時間參數的影響,即只考慮書寫間距的影響。所以大家都能用。

部分代碼如下:

                Point p1 = (Point)stylusPoints[1];   //線段的終點
                Vector dis= p1 - p0;    //兩點間距
                if (dis.Length != 0)
                {
                    double y= (p1.Y - p0.Y) / dis.Length;   //Y軸上的單位變化量
                    double x= (p1.X - p0.X) / dis.Length;
                    double aX = p0.X; 
                    double aY = p0.Y;
                    for (double j = 0; j <= dis.Length; j += 10)   //分割線段,化線為點,對每個點進行處理,達到粗細平滑變化的效果
                    {
                        if (Convert.ToInt32(dis.Length) > LastDis)  //前個線段的長度大於當前線段的情況,讓線條變細
                        {
                            t1 = (t1 - _sub) <= _minT ? _minT : t1 - _sub;
                        }
                        else if (t1 < _thick) //前個線段的長度大於當前線段,且小於規定的最大寬度
                        {
                            t1 = (t1 + _add) >= _thick ? _thick : t1 + _add;
                        }
                        aX+= x; 
                        aY+= y;

                        ImageDrawing i = new ImageDrawing()
                        {
                            ImageSource = imageSource,
                            Rect = new Rect(aX - t1/ 2.0, aY - t1/ 2.0, t1, t1)
                        };
                        i.Freeze();
                        drawingContext.DrawDrawing(i);  //繪制
                    }
                    p0 = new Point(aX, aY);  //保存最后一個優化點,即終點
                    LastDis = Convert.ToInt32(dis.Length);   
                    _thick= t1;
                }

靜態呈現方法與之相同,只要理解了上述的方法,應該就沒什么大問題了。筆者就不在此贅述。 效果圖:


可以在最小的粗細度的時候判斷是否還是超過現在的線段長度,是的話可以讓aX 和aY 這兩個值直接等於最后的點,從而實現書寫過快時的流線間斷感。但是線段會不平滑。(圖畫的比較丑,沒有表現出間斷的優美感。。。)

效果圖:

if (_thickness == _minT)
{
     aX += x * (distance.Length - j - 1);
     aY += y * (distance.Length - j - 1);
     break;
}

打完收工。

有疑問請留言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM