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图片,这个就得根据你屏幕的大小了。

时间: 2024-11-03 22:08:59

android 涂鸦(清屏,画笔,粗细,保存)以及canvas源码学习的相关文章

Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习

package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.View; public class UnderstandDispatchTouchEvent { /** * dispatchTouchEvent()源码学习及其注释 * 常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouchEvent->onTouchE

[Android FrameWork 6.0源码学习] View的重绘过程之WindowManager的addView方法

博客首页:http://www.cnblogs.com/kezhuang/p/ 关于Activity的contentView的构建过程,我在我的博客中已经分析过了,不了解的可以去看一下 <[Android FrameWork 6.0源码学习] Window窗口类分析> 本章博客是接着上边那篇博客分析,目的是为了引出分析ViewRootImpl这个类.现在只是分析完了Window和ActivityThread的调用过程 从ActivityThread到WindowManager再到ViewRoo

Android布局文件的加载过程分析:Activity.setContentView()源码分析

大家都知道在Activity的onCreate()中调用Activity.setContent()方法可以加载布局文件以设置该Activity的显示界面.本文将从setContentView()的源码谈起,分析布局文件加载所涉及到的调用链.本文所用的源码为android-19. Step 1  .Activity.setContentView(intresId) public void setContentView(int layoutResID) { getWindow().setConten

Android开发之自己定义TabHost文字及背景(源码分享)

使用TabHost 能够在一个屏幕间进行不同版面的切换,而系统自带的tabhost界面较为朴素,我们应该怎样进行自己定义改动优化呢 MainActivity的源码 package com.dream.ledong; import android.app.TabActivity; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.view.Gr

Android App 启动时显示正在加载图片(源码)

微信.QQ.天天动听等程序,在打开时显示了一张图片,然后跳转到相关界面.本文实现这个功能,其实很简单.... 新建两个Activity,LoadingActivity,MainActivity,将LoadingActivity设置为android.intent.action.MAIN.使用TimerTesk,或者Thread将LoadingActivity显示几秒后跳转到MainActivity界面. LoadingActivity: new Timer().schedule(new Timer

[Android阅读代码]android-async-http源码学习一

android-async-http 下载地址 一个比较常用的Http请求库,基于org.apache.http对http操作进行封装. 特点: 1.每一个HTTP请求发生在UI线程之外,Client通过回调处理HTTP请求的结果,使得Client代码逻辑清晰 2.每一个请求使用线程池管理执行 3.支持gzip , cookie等功能 4.支持自动重试连接功能 [Android阅读代码]android-async-http源码学习一,布布扣,bubuko.com

Android ViewGroup触摸屏事件派发机制详解与源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android View触摸屏事件派发机制详解与源码分析>,阅读本篇之前建议先阅读. 1 背景 还记得前一篇<Android View触摸屏事件派发机制详解与源码分析>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事

Android多线程研究(1)——线程基础及源码剖析

从今天起我们来看一下Android中的多线程的知识,Android入门容易,但是要完成一个完善的产品却不容易,让我们从线程开始一步步深入Android内部. 一.线程基础回顾 package com.maso.test; public class TraditionalThread { public static void main(String[] args) { /* * 线程的第一种创建方式 */ Thread thread1 = new Thread(){ @Override publi

Android Volley完全解析(四),带你从源码的角度理解Volley

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17656437 经过前三篇文章的学习,Volley的用法我们已经掌握的差不多了,但是对于Volley的工作原理,恐怕有很多朋友还不是很清楚.因此,本篇文章中我们就来一起阅读一下Volley的源码,将它的工作流程整体地梳理一遍.同时,这也是Volley系列的最后一篇文章了. 其实,Volley的官方文档中本身就附有了一张Volley的工作流程图,如下图所示. 多数朋友突然看到一张这样