CHƯƠNG III : NỘI DUNG KẾT QUẢ NGHIÊN CỨU
3.1 PHÁT TRIỂN ỨNG DỤNG
3.1.13 Chức năng tô màu
Tô màu vào vùng giới hạn cùng màu
Trong ứng dụng ta tô, vẽ lên một bitmap, bitmap này là tập hợp các điểm ảnh (pixel). Để tô màu lên một vùng giới hạn cùng màu. Ta sẽ tô màu cho từng điểm ảnh , giải thuật của ta sẽ thực hiện như sau:
- Khi chạm tay xuống màn hình, ta sẽ lấy giá trị màu của điểm vừa chạm, so sánh với màu ta cần tô, nếu 2 màu này giống nhau ta sẽ không thực hiện gì hết. Ngược lại ta sẽ thực hiện như sau:
- Trong khi hàng đợi không rỗng, lấy một điểm trong hàng đợi ra
- Tạo một điểm mới có tọa độ (x,y) bằng với tọa độ của điểm vừa lấy ra. Giảm tọa độ x của điểm này xuống cho đến khi màu của điểm này khác với màu của điểm đã chạm vào màn hình.
- Tăng tọa độ x lên, trong khi màu của điểm (x,y) vẫn giống màu của điểm tay ta chạm vào, ta thay màu của điểm này bằng màu muốn tô. Cứ mỗi khi tăng tọa độ x lên 3 đơn vị , ta sẽ xét điểm có tọa (x,y-1) và (x,y+1) (điểm trên và điểm dưới so với điểm đang xét) nếu màu của hai điểm này giống với màu của điểm ta chạm tay vào thì đưa hai điểm vào hàng đợi. Thực hiện cho đến khi hàng đợi không còn điểm nào nữa.
Để thực hiện chức năng trên, ta xây dựng một lớp mới, trong lớp này sẽ có một phương thức thực hiện như giải thuật trên. Trong ứng dụng ta xây dựng lớp MyFill, phương thức tô màu như trên ta thực hiện trong phương thức fill() . Như đã phân tích
ta cần truyền vào phương thức này một bitmap, tọa độ điểm ta chạm tay vào màn hình, màu của điểm khi tay ta chạm vào, màu ta cần tô :
public void fill(Bitmap image,Point node,int targetColor,int replacementColor) {
int width = image.getWidth(); int height = image.getHeight(); int target = targetColor;
int replacement = replacementColor;
// kiểm tra màu điểm chạm vào có giống màu cần tô hay không if (target != replacement) { // nếu khác tạo một hàng đợi
Queue<Point> queue = new LinkedList<Point>(); //thêm điểm vào hàng đợi queue.add(new Point(node.x,node.y)); //trong khi hàng đợi khác rỗng do { // lấy một điểm từ hàng đợi ra int x = node.x; int y = node.y; // giảm tọa độ x khi đến khi điểm (x,y) có màu khác với màu của điểm khi chạm tay vào màn hình while (x > 0 && image.getPixel(x - 1, y) == target) { x--;
}
while (x < width && image.getPixel(x, y) == target) { // thay đổi màu của điểm (x,y) image.setPixel(x, y, replacement); // thêm vào hàng đợi nếu thỏa điều kiện if(y>0 && x%3==0 && image.getPixel(x, y- 1)== target) {
queue.add(new Point(x,y-1)); }
if(y < height-1 && x%3==0 && image.getPixel(x, y+1) == target) { queue.add(new Point(x,y+1)); } x++; }
}
Việc thực hiện việc tô màu này được thực hiện khi ta chạm tay vào màn hình, vì vậy ta sẽ gọi phương thức này trong phương thức touch_start() trong lớp MyView.Vì có nhiều chức năng tô màu khác nên ta cũng cần một khai báo một biến để nhận biết các chức năng này.
public class MyView extends View {
int fill_number=1;//biến này để lựa chọn các chức năng tô màu
// Xây dựng một phương thức để thay đổi giá trị trên public void setFillNumber(int n)
{
fill_number = n; }
private void touch_start(float x, float y) { MyFill f = new MyFill();
switch (fill_number) { case 1:
f.fill(bitmap, p, bitmap.getPixel((int) x, (int) y),paint.getColor());
// … các lựa chọn tô màu khác break;
} }
Khi nhấn vào nút chức năng để lựa chọn cách tô màu. Màn hình sẽ hiển thị một dialog, trên đó có các ImageButton tương ứng với các cách tô màu, để lựa chọn ta nhấn vào các nút này.Vì thế ta xây dựng lớp FillDialog để thực hiện chức năng này. Tương tự các chức năng có hiển thị dialog như trên. Trong lớp FillDialog, ta xây dựng một phương thức để hiển thị và lựa chọn các cách tô màu. Bố cục các nút trên dialog này được định nghĩa trong file fill_dialog.xml. Phương thức để hiển thị và chọn các cách vẽ
trong lớp FillDialog :
public void chooseFill(final MyView mv) {
final Dialog da = new Dialog(con);
da.setContentView(R.layout.fill_dilog); da.setTitle("Type of fill");
@Override
public void onClick(View v) {
// TODO Auto-generated method stub switch (v.getId()) {
case R.id.bt_fill_all:
mv.setFillNumber(1);// xác định cách thức tô màu
da.dismiss(); break;
// các lựa chọn tô màu khác }
} };
Trong lớp BabyPaintActivity, ta tạo một đối tượng thuộc lớp FillDialog để gọi lại phương thức chooseFill(). Ta cũng xây dựng một phương thức ứng với sự kiện nhấn vào của nút chức năng này để hiển thị dialog lên màn hình :
public void Fill(View v) { da_fill.chooseFill(mview); } <ImageButton android:id="@+id/bt_fill" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/icon_fill" android:onClick="Fill"/>
Tô màu vào vùng có bán kính định trước
Chức năng này sẽ tô màu vào một vòng tròn có bán kính định trước, bán kính này có thể thay đổi khi ta thay đổi độ lớn của nét vẽ. Với chức năng này, các màu được tô vào sẽ chuyển từ đậm dần đến nhạt dần trong vùng bán kính đã định trước đó (thực tế là các điểm ảnh được tô màu sẽ thưa dần khi đi ra xa tâm). Để làm được điều này, trước tiên ta cần định nghĩa một hàm trả về khoảng cách giữa 2 điểm :
public double distance(Point a,Point b) {
double result;
result = Math.sqrt((b.x-a.x)*(b.x-a.x)+(b.y- a.y)*(b.y-a.y));
return result;
}
Sau đó ta xây dựng 2 phương thức tương ứng với 2 cách tô :
fillCircle(Bitmap bit,Point p,int radius, int
color) : phương thức này sẽ tô màu vào vòng tròn có đường kính radius, các điểm ảnh gần tâm sẽ được tô liên tiếp nhau, khi ra xa tâm các điểm ảnh sẽ được tô thưa dần
fillCircle_gradient(Bitmap bit,Point p,int radius,
int color): phương thức này sẽ tô màu vào vòng tròn có đường kính radius, các điểm ảnh sẽ được tô thưa dần từ tâm ra ngoài.
Hình 17 : Hình ảnh minh họa fillCirlle() và fillCircle_gradient()
Cơ sở để tạo những vùng tô màu như trên là ta sẽ tô chồng nhiều lớp lên nhau, trước tiên ta sẽ tô cho vòng tròn có bán kính lớn nhất, sau đó tô cho vòng tròn có bán kính nhỏ dần.
Để thực hiện vẽ được các vòng tròn như trên, ta sẽ chạy 2 vòng lặp lòng nhau để duyệt qua một vùng ảnh hình vuông có kích thước bằng với đường kính đường tròn, sau đó sử dụng phương thức xác định khoảng cách định nghĩa bên trên để giới hạn lại các điểm trong vòng tròn và tô màu cho chúng.
Để gọi 2 phương thức này ta làm tương tự như tô màu vào vùng giới hạn cùng màu. Ta chỉ cần lưu ý ở việc truyền tham số đường kính để thay đổi kích thước theo độ lớn của nét vẽ:
f.fillCircle(bitmap, p, (int)paint.getStrokeWidth()+(int)bk
,paint.getColor());