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

更新:本文的内容只是一部分,这段时间添加了橡皮擦这个新功能,于是问题接二连三的来,比如说:如果用本文的内容去做橡皮擦的话,难!(至少我没解决,不是没背景图,就是有背景图但是更新要在下一下刷橡皮擦的时候才能更新效果),然后有个setbackgroundresource的函数,这个函数就可以了,但是问题又来了,比如说保存,清屏,但是我都解决了(清屏的话就是重新构造一个图,当clear的时候就把这张图赋值给以前的图片。保存的话我就是把绘下个图放到一张有背景的canvas上面,至是分辨率的问题自己去解决就行了,保证存下来的跟你用setbackgoundresource绘图看到的效果一致,需要源码的请联系我)



本人也是在网上查了很多文章后才做出来的,感觉网上的一些涂鸦功能不是很完善,在此就稍微完善了一下。先看下效果图吧(想做成全屏的话需要弄一张跟你屏幕一样大小的背景图,找不到也没关系,我有改尺寸的代码,一并献上)。代码下载请到http://www.oschina.net/code/snippet_729469_20445   其实涂鸦的难点就是如何能在canvas上进行清屏又能保存,至少目前我碰到的情况是这样,这就需要对canvas与bitmap的较为深入的理解了,这个你多写这方面的代码就行了,网上有许多涂鸦的作品,看看源代码。

涂鸦中关于canvas的学习需要掌握三点吧:1:view 2:onDraw函数 3:onTouchEvent

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中我实现了一个菜单按钮

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)

    // 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())这个函数
private static native int initRaster(int nativeBitmapOrZero);
这个需要涉及到向底层本地函数传递一个int参数,那么这个bitmap.ni()是什么呢?查看bitmap.java同样位于android2.3.3/frameworks/base/graphics/java/android/graphics
/* 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,注册函数有

{"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)
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) 

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,这个是在注册函数中表示的对应的函数

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中的
canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint);
类似的,其他的一些canvas的操作都是调用的SKCanvas的对应的方法。(skcanvas与skia的关系请大家网上查询)

那么SKCnvas的源码究竟存放在哪里呢?android/external/skia/src/core,因为所有的方法的实现都是类似的,这里我就单独选择一个简单的drawLine吧

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
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
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方法
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循环
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函数
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。。。。图形化的东西了解太少了,鄙人就做抛砖引玉的作用吧,分析到此结束,希望大侠补充了。

最后,在背景图上需要改大小的,这里有代码

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


转载于:https://my.oschina.net/JumpLong/blog/123226

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值