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

更新:本文的內容只是一部分,這段時間添加了橡皮擦這個新功能,因而問題連續不斷的來,好比說:若是用本文的內容去作橡皮擦的話,難!(至少我沒解決,不是沒背景圖,就是有背景圖可是更新要在下一下刷橡皮擦的時候才能更新效果),而後有個setbackgroundresource的函數,這個函數就能夠了,可是問題又來了,好比說保存,清屏,可是我都解決了(清屏的話就是從新構造一個圖,當clear的時候就把這張圖賦值給之前的圖片。保存的話我就是把繪下個圖放到一張有背景的canvas上面,至是分辨率的問題本身去解決就好了,保證存下來的跟你用setbackgoundresource繪圖看到的效果一致,須要源碼的請聯繫我) java



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

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

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 app

canvas(Bitmap bitmap) less

    // 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,註冊函數有 ide

{"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吧 ui

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圖片,這個就得根據你屏幕的大小了。
相關文章
相關標籤/搜索