2012-11-23 37 views
32

Tôi có chế độ xem thực hiện một số bản vẽ cơ bản. Sau đó tôi muốn vẽ một hình chữ nhật với một lỗ được đục lỗ để chỉ có thể nhìn thấy một vùng của bản vẽ trước đó. Và tôi muốn làm điều này với khả năng tăng tốc phần cứng được bật cho chế độ xem của tôi để có hiệu suất tốt nhất.Đấm một lỗ trong lớp phủ hình chữ nhật với tăng tốc HW được bật trên Chế độ xem

Hiện tại tôi có hai phương pháp hoạt động, nhưng chỉ hoạt động khi tăng tốc phần cứng bị vô hiệu hóa và phương thức kia quá chậm.

Phương pháp 1: SW Acceleration (Chậm)

final int saveCount = canvas.save(); 

// Clip out a circle. 
circle.reset(); 
circle.addCircle(cx, cy, radius, Path.Direction.CW); 
circle.close(); 
canvas.clipPath(circle, Region.Op.DIFFERENCE); 

// Draw the rectangle color. 
canvas.drawColor(backColor); 

canvas.restoreToCount(saveCount); 

này không hoạt động với khả năng tăng tốc phần cứng cho chế độ xem vì 'canvas.clipPath' không được hỗ trợ trong chế độ này (Tôi biết tôi có thể buộc SW rendering, nhưng tôi muốn tránh điều đó).

Cách 2: HW Acceleration (V. chậm)

// Create a new canvas. 
final Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 
final Canvas c = new Canvas(b); 

// Draw the rectangle colour. 
c.drawColor(backColor); 

// Erase a circle. 
c.drawCircle(cx, cy, radius, eraser); 

// Draw the bitmap on our views canvas. 
canvas.drawBitmap(b, 0, 0, null); 

đâu tẩy được tạo ra như

eraser = new Paint() 
eraser.setColor(0xFFFFFFFF); 
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 

này rõ ràng là chậm - một mới Bitmap kích thước của quan điểm là tạo ra mọi cuộc gọi vẽ.

Phương pháp 3: HW Acceleration (Fast, không hoạt động trên một số thiết bị)

canvas.drawColor(backColor); 
canvas.drawCircle(cx, cy, radius, eraser); 

Tương tự như các HW tăng tốc phương pháp tương thích, nhưng không cần vải thêm. Có một vấn đề lớn với điều này mặc dù - nó hoạt động với SW rendering buộc, nhưng trên HTC One X (Android 4.0.4 - và có lẽ một số thiết bị khác) ít nhất với HW rendering cho phép nó rời khỏi vòng tròn hoàn toàn màu đen. Điều này có thể liên quan đến 22361.

Phương pháp 4: HW Acceleration (chấp nhận, hoạt động trên tất cả các thiết bị)

Theo đề nghị của Jan để cải thiện phương pháp 2, tôi tránh tạo bitmap trong mỗi cuộc gọi đến onDraw, thay vì làm như vậy trong onSizeChanged:

if (w != oldw || h != oldh) { 
    b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 
    c = new Canvas(b); 
} 

Và sau đó chỉ cần sử dụng những trong onDraw:

if (overlayBitmap == null) { 
    b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 
    c = new Canvas(b); 
} 
b.eraseColor(Color.TRANSPARENT); 
c.drawColor(backColor); 
c.drawCircle(cx, cy, radius, eraser); 
canvas.drawBitmap(b, 0, 0, null); 
.210

Việc thực hiện là không tốt như phương pháp 3, nhưng tốt hơn nhiều so với 2 và tốt hơn một chút so với 1.

Câu hỏi đặt ra

Làm thế nào tôi có thể đạt được hiệu quả tương tự, nhưng làm như vậy một cách tương thích với HW tăng tốc (VÀ hoạt động liên tục trên các thiết bị)? Một phương pháp làm tăng hiệu suất kết xuất SW cũng sẽ được chấp nhận.

NB: Khi di chuyển vòng tròn xung quanh tôi chỉ làm mất hiệu lực một vùng - không phải toàn bộ canvas - vì vậy không có chỗ cho cải thiện hiệu suất ở đó.

+0

Chỉ đơn giản là bộ nhớ đệm 'Bitmap b' và' Canvas c' có thể? Nó có giúp được không? –

+0

Tôi không nghĩ vậy - nếu bạn chỉ làm 'b' và' c' nói trong 'onSizeChanged' và không phải trong' onDraw' thì bạn sẽ vẽ nhiều lần trên cùng một khung hình mà không xóa (vì vậy bạn muốn kết thúc với nhiều lớp). Trên thực tế nó sẽ làm việc nếu tôi có thể xóa 'b' trong' onDraw' mà cũng có thể có được - Tôi sẽ thử nó, cảm ơn! –

+0

@Jan: Cảm ơn điều này đã làm việc - tôi đã tạo Bitmap 'b' và Canvas' c' trong 'onSizeChanged', sau đó tôi chỉ cần gọi' b.eraseColor (Color.Transparent) 'trước khi thực hiện bất kỳ bản vẽ nào để xóa bất kỳ bản vẽ trước đó trên 'c'. Đăng bình luận của bạn như là một câu trả lời và tôi sẽ đánh dấu nó là chính xác nếu tôi không nhận được bất kỳ câu trả lời hữu ích hơn trong ngày hôm sau hay như vậy. Nó vẫn không trơn tru như phương pháp 3, nhưng tốt hơn nhiều so với 2. –

Trả lời

17

Thay vì phân bổ canvas mới trên mỗi lần sơn lại, bạn sẽ có thể phân bổ nó một lần và sau đó sử dụng lại canvas trên mỗi lần sơn lại.

trên init và đã đổi kích thước:

Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); 
Canvas c = new Canvas(b); 

trên repaint:

b.eraseColor(Color.TRANSPARENT); 
    // needed if backColor is not opaque; thanks @JosephEarl 
c.drawColor(backColor); 
c.drawCircle(cx, cy, radius, eraser); 

canvas.drawBitmap(b, 0, 0, null); 
+0

Giải pháp Superrb –

+0

Hơi muộn một chút, tuy nhiên tôi có một câu hỏi về điều đó. Tôi muốn làm điều tương tự (lỗ trong hình chữ nhật), tuy nhiên vấn đề của tôi là quan điểm của tôi cần chiếm toàn bộ màn hình, do đó bitmap được tạo sẽ có kích thước màn hình khoảng 4 MB - 10 MB tùy thuộc vào trên kích thước của màn hình. Tạo bitmap lớn như vậy có thể gây ra lỗi OutOfMemoryError. Có cách giải quyết nào về điều đó không? – MScott

+0

@MScott Tôi không thể tưởng tượng rằng 10 MB sẽ là bất kỳ thách thức nào đối với bất kỳ điện thoại thông minh nào của ngày hôm nay để phân bổ. Đừng quên rằng GPU phải giữ lượng bộ nhớ này chỉ để nó có thể nạp vào màn hình LCD. Hai lần, rất có thể - một bản sao để hiển thị cho người dùng, một bản sao khác được cập nhật bởi phần mềm trong thời gian chờ đợi. Bạn có thể fiddle với sơn bốn hình chữ nhật, một cho mỗi bên, nhưng nó sẽ không hoạt động tốt với antialiasing nếu họ là bán trong suốt và nó chỉ có thể sản xuất lỗ hình chữ nhật. –