android 塗鴉(清屏,畫筆,粗細,保存)以及canvas源碼學習


更新:本文的內容只是一部分,這段時間添加了橡皮擦這個新功能,於是問題接二連三的來,比如說:如果用本文的內容去做橡皮擦的話,難!(至少我沒解決,不是沒背景圖,就是有背景圖但是更新要在下一下刷橡皮擦的時候才能更新效果),然后有個setbackgroundresource的函數,這個函數就可以了,但是問題又來了,比如說保存,清屏,但是我都解決了(清屏的話就是重新構造一個圖,當clear的時候就把這張圖賦值給以前的圖片。保存的話我就是把繪下個圖放到一張有背景的canvas上面,至是分辨率的問題自己去解決就行了,保證存下來的跟你用setbackgoundresource繪圖看到的效果一致,需要源碼的請聯系我)

 

 

本人也是在網上查了很多文章后才做出來的,感覺網上的一些塗鴉功能不是很完善,在此就稍微完善了一下。先看下效果圖吧(想做成全屏的話需要弄一張跟你屏幕一樣大小的背景圖,找不到也沒關系,我有改尺寸的代碼,一並獻上)。代碼下載請到http://www.oschina.net/code/snippet_729469_20445   其實塗鴉的難點就是如何能在canvas上進行清屏又能保存,至少目前我碰到的情況是這樣,這就需要對canvas與bitmap的較為深入的理解了,這個你多寫這方面的代碼就行了,網上有許多塗鴉的作品,看看源代碼。

塗鴉中關於canvas的學習需要掌握三點吧:1:view 2:onDraw函數 3:onTouchEvent

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public class HandWrite extends View
{
     Paint paint = null ;
     private Bitmap originalBitmap = null ;
      Bitmap new1Bitmap = null ;
     private Bitmap new2Bitmap = null ;
     private float clickX = 0 ,clickY = 0 ;
     private float startX = 0 ,startY = 0 ;
     private boolean isMove = true ;
     private boolean isClear = false ;
     int color = Color.WHITE;
      float strokeWidth = 3 .0f;
     public HandWrite(Context context, AttributeSet attrs)
     {
         super (context, attrs);
//      originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.t);
         originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a).copy(Bitmap.Config.ARGB_8888, true );
         new1Bitmap = Bitmap.createBitmap(originalBitmap);
     }
     
 
     public void clear(){
         isClear = true ;
         new2Bitmap = Bitmap.createBitmap(originalBitmap);
         invalidate();
     }
     public void setstyle( float strokeWidth){
         this .strokeWidth = strokeWidth;
     }
     @Override
     protected void onDraw(Canvas canvas)
     {
         super .onDraw(canvas);
         canvas.drawBitmap(HandWriting(new1Bitmap), 0 , 0 , null );
         
     }
 
     public Bitmap HandWriting(Bitmap originalBitmap)
     {
         Canvas canvas = null ;
         
         if (isClear){
             canvas = new Canvas(new2Bitmap);
         }
         else {
             canvas = new Canvas(originalBitmap);
         }
         paint = new Paint();
         paint.setStyle(Style.STROKE);
         paint.setAntiAlias( true );
         paint.setColor(color);
         paint.setStrokeWidth(strokeWidth);
         if (isMove){
             canvas.drawLine(startX, startY, clickX, clickY, paint);
         }
         
         startX = clickX;
         startY = clickY;
         
         if (isClear){
             return new2Bitmap;
         }
         return originalBitmap;
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event)
     {
         clickX = event.getX();
         clickY = event.getY();
         if (event.getAction() == MotionEvent.ACTION_DOWN){
             
             isMove = false ;
             invalidate();
             return true ;
         }
         else if (event.getAction() == MotionEvent.ACTION_MOVE){
             
             isMove = true ;
             invalidate();
             return true ;
         }
         
         return super .onTouchEvent(event);
     }

這個view的代碼網上有,這里面必須得實現兩個重要的方法,一個是onDraw一個是onTouchEvent,onDraw是在你每次觸碰屏幕的時候都會觸發,包括你初始化的時候。onTouchEvent就是在你觸碰屏幕后采取的相應操作。其實塗鴉的關鍵就是通過drawLine將瞬間變化的兩點連起來畫成直線,然后畫在canvas上的bitmap上。

在mainActivity中我實現了一個菜單按鈕

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
public class CanvasDrawActivity extends Activity
{
     private static final String TAG = "CanvasDrawActivity" ;
     /** Called when the activity is first created. */
     private int width;
     private int height;
     private HandWrite handWrite = null ;
     private Button clear = null ;
     private int whichColor = 0 ;
     private int whichStrokeWidth = 0 ;
 
     @Override
     public void onCreate(Bundle savedInstanceState)
     {
         super .onCreate(savedInstanceState);
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                 WindowManager.LayoutParams.FLAG_FULLSCREEN);
 
         setContentView(R.layout.main);
         
         handWrite = (HandWrite)findViewById(R.id.handwriteview);
 
     }
      
     
     
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         // TODO Auto-generated method stub
         menu.add( 0 , 1 , 1 , "清屏" );
         menu.add( 0 , 2 , 2 , "顏色" );                
         menu.add( 0 , 3 , 3 , "畫筆" );
         menu.add( 0 , 4 , 4 , "保存" );
         return super .onCreateOptionsMenu(menu);
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         // TODO Auto-generated method stub
         if (item.getItemId() == 4 ){
 
             File f = new File(Environment.getExternalStorageDirectory()
                     .getAbsolutePath() + "/aaa.jpg" );
             try {
 
                 saveMyBitmap(f, handWrite.new1Bitmap);
 
             } catch (IOException e) {
                 e.printStackTrace();
             }
          
          
         } else if (item.getItemId() == 1 ){
 
             handWrite.clear();           
 
         } else if (item.getItemId() == 2 ){
             
             Dialog mDialog = new AlertDialog.Builder(CanvasDrawActivity. this )
             .setTitle( "顏色設置" )
             .setSingleChoiceItems( new String[]{ "白色" , "綠色" , "紅色" }, whichColor, new DialogInterface.OnClickListener()
             {
                 
                 @Override
                 public void onClick(DialogInterface dialog, int which)
                 {
                     // TODO Auto-generated method stub
                     switch (which)
                     {
                         case 0 :
                         {
                           
                             handWrite.color = Color.WHITE;
                             whichColor = 0 ;
                             break ;
                         }
                         case 1 :
                         {
                            
                             handWrite.color = Color.GREEN;
                             whichColor = 1 ;
                             break ;
                         }
                         case 2 :
                         {
 
                             handWrite.color = Color.RED;
                             whichColor = 2 ;
                             break ;
                         }
                     }
                 }
             })
             .setPositiveButton( "確定" , new DialogInterface.OnClickListener()
             {
                 
                 @Override
                 public void onClick(DialogInterface dialog, int which)
                 {
                     // TODO Auto-generated method stub
                     dialog.dismiss();
                 }
             })
             .create();
             mDialog.show();
             
             
             
         } else if (item.getItemId() == 3 ){
             
 
             Dialog mDialog = new AlertDialog.Builder(CanvasDrawActivity. this )
              .setTitle( "畫筆設置" )
              .setSingleChoiceItems( new String[]{ "細" , "中" , "粗" }, whichStrokeWidth, new DialogInterface.OnClickListener()
              {
                  
                  @Override
                  public void onClick(DialogInterface dialog, int which)
                  {
                      // TODO Auto-generated method stub
                      switch (which)
                      {
                          case 0 :
                          {
                              
                              handWrite.strokeWidth = 3 .0f;
                              whichStrokeWidth = 0 ;
                              break ;
                          }
                          case 1 :
                          {
                             
                              handWrite.strokeWidth = 6 .0f;  
                              whichStrokeWidth = 1 ;
                              break ;
                          }
                          case 2 :
                          {
                              handWrite.strokeWidth = 9 .0f;
                              whichStrokeWidth = 2 ;
                              break ;
                          }
                      }
                  }
              })
              .setPositiveButton( "確定" , new DialogInterface.OnClickListener()
              {
                  
                  @Override
                  public void onClick(DialogInterface dialog, int which)
                  {
                      // TODO Auto-generated method stub
                      dialog.dismiss();
                  }
              })
              .create();
              mDialog.show();
             
             
         }
         return super .onOptionsItemSelected(item);
     }
     
     
     public void saveMyBitmap(File f, Bitmap mBitmap) throws IOException {
         try {
             f.createNewFile();
             FileOutputStream fOut = null ;
             fOut = new FileOutputStream(f);
             mBitmap.compress(Bitmap.CompressFormat.PNG, 100 , fOut);
             fOut.flush();
             fOut.close();
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
     
     
}

這些就是通過菜單選項對相應的canvas上面的paint進行設置即可達到效果。

那么現在就看看塗鴉這個程序上與canvas相關的幾個函數的源碼吧!(初始函數,drawLine,drawBitmap這3個)canvas.java文件位於android2.3.3/frameworks/base/graphics/java/android/graphics

canvas(Bitmap bitmap)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
     // assigned in constructors, freed in finalizer
     final int mNativeCanvas;
     
 
     private Bitmap  mBitmap;    // if not null, mGL must be null
   
 
     // Package-scoped for quick access.
    int mDensity = Bitmap.DENSITY_NONE;
 
     public Canvas(Bitmap bitmap) {
         if (!bitmap.isMutable()) {
             throw new IllegalStateException(
                             "Immutable bitmap passed to Canvas constructor" );
         }
         throwIfRecycled(bitmap);
         mNativeCanvas = initRaster(bitmap.ni());
         mBitmap = bitmap;
         mDensity = bitmap.mDensity;
     }

這個初始化比較簡單,就主要是一個叫做initRaster(bitmap.ni())這個函數

?
1
private static native int initRaster( int nativeBitmapOrZero);

這個需要涉及到向底層本地函數傳遞一個int參數,那么這個bitmap.ni()是什么呢?查看bitmap.java同樣位於android2.3.3/frameworks/base/graphics/java/android/graphics

?
1
2
3
4
5
6
7
     /* package */ final int ni() {
         return mNativeBitmap;
     }
 
     // Note:  mNativeBitmap is used by FaceDetector_jni.cpp
     // Don't change/rename without updating FaceDetector_jni.cpp
     private final int mNativeBitmap;

也就是想下面傳遞的是mNativeBitmap這個值,而這個值會在FaceDetector_jni.cpp中使用。

那么initRaster怎么實現的呢,看Canvas.cpp位於android/frameworks/base/core/jni/android/graphics,注冊函數有

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
     { "initRaster" , "(I)I" , ( void *) SkCanvasGlue::initRaster},
     { "native_drawBitmap" , "(IIFFIIII)V" ,
         ( void *) SkCanvasGlue::drawBitmap__BitmapFFPaint},
     { "native_drawLine" , "(IFFFFI)V" , ( void *) SkCanvasGlue::drawLine__FFFFPaint},
     { "native_drawColor" , "(III)V" , ( void *) SkCanvasGlue::drawColor__II},
     { "native_drawPaint" , "(II)V" , ( void *) SkCanvasGlue::drawPaint},
     { "drawPoint" , "(FFLandroid/graphics/Paint;)V" ,
     ( void *) SkCanvasGlue::drawPoint},
     { "drawPoints" , "([FIILandroid/graphics/Paint;)V" ,
         ( void *) SkCanvasGlue::drawPoints},
     { "drawLines" , "([FIILandroid/graphics/Paint;)V" ,
         ( void *) SkCanvasGlue::drawLines},
     { "native_drawLine" , "(IFFFFI)V" , ( void *) SkCanvasGlue::drawLine__FFFFPaint},
     { "native_drawRect" , "(ILandroid/graphics/RectF;I)V" ,
         ( void *) SkCanvasGlue::drawRect__RectFPaint},
     { "native_drawRect" , "(IFFFFI)V" , ( void *) SkCanvasGlue::drawRect__FFFFPaint},
     { "native_drawOval" , "(ILandroid/graphics/RectF;I)V" ,
         ( void *) SkCanvasGlue::drawOval},
     { "native_drawCircle" , "(IFFFI)V" , ( void *) SkCanvasGlue::drawCircle},
     { "native_drawArc" , "(ILandroid/graphics/RectF;FFZI)V" ,

其實這些就是對canva的常見操作,包括下面遇到的drawline,drawBitmap,請看: drawBitmap(Bitmap bitmap,float, left,float top, Paint ,paint)

?
1
2
3
4
5
6
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
     throwIfRecycled(bitmap);
     native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top,
             paint != null ? paint.mNativePaint : 0 , mDensity, mScreenDensity,
             bitmap.mDensity);
}

 drawLine(float startX, float startY, float stopX, float stopY, Paint paint) 

?
1
2
3
4
5
public void drawLine( float startX, float startY, float stopX, float stopY,
                      Paint paint) {
     native_drawLine(mNativeCanvas, startX, startY, stopX, stopY,
                     paint.mNativePaint);
}

他們實際上都是使用的SkCanvasGlue中的對應函數,而這個時候,canvas已經不再撒過去那個canvas了,它換成了SKCanvas。

比如說drawBitmap,這個是在注冊函數中表示的對應的函數

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
                                           SkCanvas* canvas, SkBitmap* bitmap,
                                           jfloat left, jfloat top,
                                           SkPaint* paint, jint canvasDensity,
                                           jint screenDensity, jint bitmapDensity) {
         SkScalar left_ = SkFloatToScalar(left);
         SkScalar top_ = SkFloatToScalar(top);
 
         if (canvasDensity == bitmapDensity || canvasDensity == 0
                 || bitmapDensity == 0) {
             if (screenDensity != 0 && screenDensity != bitmapDensity) {
                 SkPaint filteredPaint;
                 if (paint) {
                     filteredPaint = *paint;
                 }
                 filteredPaint.setFilterBitmap( true );
                 canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint);
             } else {
                 canvas->drawBitmap(*bitmap, left_, top_, paint);
             }
         } else {
             canvas->save();
             SkScalar scale = SkFloatToScalar(canvasDensity / ( float )bitmapDensity);
             canvas->translate(left_, top_);
             canvas->scale(scale, scale);
 
             SkPaint filteredPaint;
             if (paint) {
                 filteredPaint = *paint;
             }
             filteredPaint.setFilterBitmap( true );
 
             canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
 
             canvas->restore();
         }
     }

它最終調用的就是SKCanvas中的

?
1
canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint);

類似的,其他的一些canvas的操作都是調用的SKCanvas的對應的方法。(skcanvas與skia的關系請大家網上查詢)

那么SKCnvas的源碼究竟存放在哪里呢?android/external/skia/src/core,因為所有的方法的實現都是類似的,這里我就單獨選擇一個簡單的drawLine吧

?
1
2
3
4
5
6
7
8
void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
                         const SkPaint& paint) {
     SkPoint pts[2];
     
     pts[0].set(x0, y0);
     pts[1].set(x1, y1);
     this ->drawPoints(kLines_PointMode, 2, pts, paint);
}

就是將亮點的坐標值存放在一個SKPoint數組中然后作為參數傳遞給drawPoints函數,繼續找drawPoints

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
                           const SkPaint& paint) {
     if (( long )count <= 0) {
         return ;
     }
 
     SkASSERT(pts != NULL);
 
     ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
     
     while (iter.next()) {
         iter.fDevice->drawPoints(iter, mode, count, pts, paint);
     }
     
     ITER_END
}

首先確保這個數組的非空的存在性,然后用一個迭代器去不斷的drawPoint,因為fDevice十一個SKDevice*類型。然后查看DKDevice.cpp文件,同樣位於android/external/skia/src/core

?
1
2
3
4
void SkDevice::drawPoints( const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
                               const SkPoint pts[], const SkPaint& paint) {
     draw.drawPoints(mode, count, pts, paint);
}

調用的是SkDraw中的drawPoints方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
                         const SkPoint pts[], const SkPaint& paint) const {
     // if we're in lines mode, force count to be even
     if (SkCanvas::kLines_PointMode == mode) {
         count &= ~( size_t )1;
     }
 
     if (( long )count <= 0) {
         return ;
     }
     
     SkAutoRestoreBounder arb;
 
     if (fBounder) {
         if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
             return ;
         }
         // clear the bounder for the rest of this function, so we don't call it
         // again later if we happen to call ourselves for drawRect, drawPath,
         // etc.
         arb.clearBounder( this );
     }
 
     SkASSERT(pts != NULL);
     SkDEBUGCODE( this ->validate();)
     
      // nothing to draw
     if (fClip->isEmpty() ||
         (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
         return ;
     }
 
     PtProcRec rec;
     if (rec.init(mode, paint, fMatrix, fClip)) {
         SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
 
         SkPoint             devPts[MAX_DEV_PTS];
         const SkMatrix*     matrix = fMatrix;
         SkBlitter*          bltr = blitter.get();
         PtProcRec::Proc     proc = rec.chooseProc(bltr);
         // we have to back up subsequent passes if we're in polygon mode
         const size_t backup = (SkCanvas::kPolygon_PointMode == mode);
         
         do {
             size_t n = count;
             if (n > MAX_DEV_PTS) {
                 n = MAX_DEV_PTS;
             }
             matrix->mapPoints(devPts, pts, n);
             proc(rec, devPts, n, bltr);
             pts += n - backup;
             SkASSERT(count >= n);
             count -= n;
             if (count > 0) {
                 count += backup;
             }
         } while (count != 0);
     } else {
         switch (mode) {
             case SkCanvas::kPoints_PointMode: {
                 // temporarily mark the paint as filling.
                 SkAutoPaintStyleRestore restore(paint, SkPaint::kFill_Style);
 
                 SkScalar width = paint.getStrokeWidth();
                 SkScalar radius = SkScalarHalf(width);
                 
                 if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
                     SkPath      path;
                     SkMatrix    preMatrix;
                     
                     path.addCircle(0, 0, radius);
                     for ( size_t i = 0; i < count; i++) {
                         preMatrix.setTranslate(pts[i].fX, pts[i].fY);
                         // pass true for the last point, since we can modify
                         // then path then
                         this ->drawPath(path, paint, &preMatrix, (count-1) == i);
                     }
                 } else {
                     SkRect  r;
                     
                     for ( size_t i = 0; i < count; i++) {
                         r.fLeft = pts[i].fX - radius;
                         r.fTop = pts[i].fY - radius;
                         r.fRight = r.fLeft + width;
                         r.fBottom = r.fTop + width;
                         this ->drawRect(r, paint);
                     }
                 }
                 break ;
             }
             case SkCanvas::kLines_PointMode:
             case SkCanvas::kPolygon_PointMode: {
                 count -= 1;
                 SkPath path;
                 SkPaint p(paint);
                 p.setStyle(SkPaint::kStroke_Style);
                 size_t inc = (SkCanvas::kLines_PointMode == mode) ? 2 : 1;
                 for ( size_t i = 0; i < count; i += inc) {
                     path.moveTo(pts[i]);
                     path.lineTo(pts[i+1]);
                     this ->drawPath(path, p, NULL, true );
                     path. rewind ();
                 }
                 break ;
             }
         }
     }
}

我起初看的時候瞬間想砸電腦,這得耽誤我晚上的dota時間啊,但是細看,你會注意到那個case語句,因為我們分析的是drawLine函數,而drawLine函數傳入的是kLines_PointMode,那么我們就分析這個語句,其實就這一個for循環

?
1
2
3
4
5
6
for ( size_t i = 0; i < count; i += inc) {
     path.moveTo(pts[i]);
     path.lineTo(pts[i+1]);
     this ->drawPath(path, p, NULL, true );
     path. rewind ();
}

它再一次不甘寂寞地調用了SKpath的兩個函數以及自身的drawPath函數

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
void SkPath::moveTo(SkScalar x, SkScalar y) {
     SkDEBUGCODE( this ->validate();)
 
     int      vc = fVerbs.count();
     SkPoint* pt;
 
     if (vc > 0 && fVerbs[vc - 1] == kMove_Verb) {
         pt = &fPts[fPts.count() - 1];
     } else {
         pt = fPts.append();
         *fVerbs.append() = kMove_Verb;
     }
     pt->set(x, y);
 
     fBoundsIsDirty = true ;
}
 
void SkPath::lineTo(SkScalar x, SkScalar y) {
     SkDEBUGCODE( this ->validate();)
 
     if (fVerbs.count() == 0) {
         fPts.append()->set(0, 0);
         *fVerbs.append() = kMove_Verb;
     }
     fPts.append()->set(x, y);
     *fVerbs.append() = kLine_Verb;
 
     fBoundsIsDirty = true ;
}
 
 
 
 
void SkDraw::drawPath( const SkPath& origSrcPath, const SkPaint& paint,
                       const SkMatrix* prePathMatrix, bool pathIsMutable) const {
     SkDEBUGCODE( this ->validate();)
 
     // nothing to draw
     if (fClip->isEmpty() ||
         (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
         return ;
     }
 
     SkPath*         pathPtr = (SkPath*)&origSrcPath;
     bool            doFill = true ;
     SkPath          tmpPath;
     SkMatrix        tmpMatrix;
     const SkMatrix* matrix = fMatrix;
 
     if (prePathMatrix) {
         if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style ||
                 paint.getRasterizer()) {
             SkPath* result = pathPtr;
     
             if (!pathIsMutable) {
                 result = &tmpPath;
                 pathIsMutable = true ;
             }
             pathPtr->transform(*prePathMatrix, result);
             pathPtr = result;
         } else {
             if (!tmpMatrix.setConcat(*matrix, *prePathMatrix)) {
                 // overflow
                 return ;
             }
             matrix = &tmpMatrix;
         }
     }
     // at this point we're done with prePathMatrix
     SkDEBUGCODE(prePathMatrix = ( const SkMatrix*)0x50FF8001;)
         
     /*
         If the device thickness < 1.0, then make it a hairline, and
         modulate alpha if the thickness is even smaller (e.g. thickness == 0.5
         should modulate the alpha by 1/2)
     */
 
     SkAutoPaintRestoreColorStrokeWidth aprc(paint);
     
     // can we approximate a thin (but not hairline) stroke with an alpha-modulated
     // hairline? Only if the matrix scales evenly in X and Y, and the device-width is
     // less than a pixel
     if (paint.getStyle() == SkPaint::kStroke_Style && paint.getXfermode() == NULL) {
         SkScalar width = paint.getStrokeWidth();
         if (width > 0 && map_radius(*matrix, &width)) {
             int scale = ( int )SkScalarMul(width, 256);
             int alpha = paint.getAlpha() * scale >> 8;
             
             // pretend to be a hairline, with a modulated alpha
             ((SkPaint*)&paint)->setAlpha(alpha);
             ((SkPaint*)&paint)->setStrokeWidth(0);
         }
     }
     
     if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
         doFill = paint.getFillPath(*pathPtr, &tmpPath);
         pathPtr = &tmpPath;
     }
     
     if (paint.getRasterizer()) {
         SkMask  mask;
         if (paint.getRasterizer()->rasterize(*pathPtr, *matrix,
                             &fClip->getBounds(), paint.getMaskFilter(), &mask,
                             SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
             this ->drawDevMask(mask, paint);
             SkMask::FreeImage(mask.fImage);
         }
         return ;
     }
 
     // avoid possibly allocating a new path in transform if we can
     SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
 
     // transform the path into device space
     pathPtr->transform(*matrix, devPathPtr);
 
     SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
 
     // how does filterPath() know to fill or hairline the path??? <mrr>
     if (paint.getMaskFilter() &&
             paint.getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fClip,
                                               fBounder, blitter.get())) {
         return ; // filterPath() called the blitter, so we're done
     }
 
     if (fBounder && !fBounder->doPath(*devPathPtr, paint, doFill)) {
         return ;
     }
 
     if (doFill) {
         if (paint.isAntiAlias()) {
             SkScan::AntiFillPath(*devPathPtr, *fClip, blitter.get());
         } else {
             SkScan::FillPath(*devPathPtr, *fClip, blitter.get());
         }
     } else {    // hairline
         if (paint.isAntiAlias()) {
             SkScan::AntiHairPath(*devPathPtr, fClip, blitter.get());
         } else {
             SkScan::HairPath(*devPathPtr, fClip, blitter.get());
         }
     }
}

再調用skmatrix,skmask,skscan。。。。圖形化的東西了解太少了,鄙人就做拋磚引玉的作用吧,分析到此結束,希望大俠補充了。

最后,在背景圖上需要改大小的,這里有代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class test {
 
     /**
      * @param args
      */
     public static void main(String[] args) {
         // TODO Auto-generated method stub
 
         BufferedImage image;
         try {
             image = ImageIO.read( new File( "D:\\t.JPG" ));
             resize(image, 300 , 300 );
 
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
        
         
         
     }
 
     private static void resize(BufferedImage source, int targetW, int targetH) throws IOException {
         // TODO Auto-generated method stub
 
             int type = source.getType(); 
             BufferedImage target = null
             double sx = ( double ) targetW / source.getWidth(); 
             double sy = ( double ) targetH / source.getHeight(); 
             // 這里想實現在targetW,targetH范圍內實現等比縮放。如果不需要等比縮放 
             // 則將下面的if else語句注釋即可 
//          if (sx > sy) 
//          { 
//              sx = sy; 
//              targetW = (int) (sx * source.getWidth()); 
//          } 
//          else 
//          { 
//              sy = sx; 
//              targetH = (int) (sy * source.getHeight()); 
//          } 
//          if (type == BufferedImage.TYPE_CUSTOM) 
//          { // handmade 
                 ColorModel cm = source.getColorModel(); 
                 WritableRaster raster = cm.createCompatibleWritableRaster(targetW, 
                         targetH); 
                 boolean alphaPremultiplied = cm.isAlphaPremultiplied(); 
                 target = new BufferedImage(cm, raster, alphaPremultiplied, null ); 
//          } 
//          else 
//          { 
//              //固定寬高,寬高一定要比原圖片大 
//              //target = new BufferedImage(targetW, targetH, type); 
//              target = new BufferedImage(800, 600, type); 
//          } 
               
             Graphics2D g = target.createGraphics(); 
               
             //寫入背景 
             g.drawImage(ImageIO.read( new File( "D:\\t.jpg" )), 0 , 0 , null ); 
               
             // smoother than exlax: 
             g.setRenderingHint(RenderingHints.KEY_RENDERING, 
                     RenderingHints.VALUE_RENDER_QUALITY); 
             g.drawRenderedImage(source, AffineTransform.getScaleInstance(sx, sy)); 
             g.dispose(); 
             ImageIO.write(target, "png" , new FileOutputStream( "D:\\a.JPG" ));
           
     }

這個java工程就是把D盤的t.jpg圖片改成300*300的a.jpg圖片,這個就得根據你屏幕的大小了。


免責聲明!

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



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