Phần 8 (tt) : Hiệu ứng - Effect
11. PerspectiveTransform :
PerspectiveTransform là hiệu ứng thường được sử dụng để tạo ra sự quay input theo hướng của trục z. Nó làm biến dạng input bằng cách di chuyển các điểm góc đến vị trí được chỉ ra và định vị lại các pixel theo các đường thẳng được nối từ các góc.
a.Các thuộc tính :
kiểu
|
Tên thuộc tính
|
Giá trị mặc định
|
Miêu tả
|
Effect
|
input
|
null
|
input của perspectiveTransform
|
DoubleProperty
|
llx
|
0
|
tọa độ x của góc trái thấp nhất của input.
|
DoubleProperty
|
lly
|
0
|
tọa độ y của góc trái thấp nhất của input.
|
DoubleProperty
|
ulx
|
0
|
tọa độ x của góc trái cao nhất của input.
|
DoubleProperty
|
uly
|
0
|
tọa độ y của góc trái cao nhất của input.
|
DoubleProperty
|
lrx
|
0
|
tọa độ x của góc phải thấp nhất của input.
|
DoubleProperty
|
lry
|
0
|
tọa độ y của góc phải thấp nhất của input.
|
DoubleProperty
|
urx
|
0
|
tọa độ x của góc phải cao nhất của input.
|
DoubleProperty
|
ury
|
0
|
tọa độ y của góc phải cao nhất của input.
|
b. Cách sử dụng :
// Tạo đối tượng PerspectiveTransform với giá trị thuộc tính mặc định
PerspectiveTransform perspectiveTrasform = new PerspectiveTransform();
// Tạo đối tượng PerspectiveTransform với :
// - tọa độ góc trái cao nhất (20,20)
// - tọa độ góc phải cao nhất (100,40)
// - tọa độ góc trái thấp nhất (20,300)
// - tọa độ góc phải thấp nhất (100,240)
PerspectiveTransform perspectiveTrasform =
new PerspectiveTransform(20,20, 100,40, 20,300, 100, 240);
|
c. Một số phương thức của PerspectiveTransform:
kiểu trả về
|
Phương thức
|
Miêu tả
|
void
|
setInput(Effect value)
|
Cung cấp input cho PerspectiveTransform effect.
|
Effect
|
getInput ()
|
Trả về input dành cho PerspectiveTransform .
|
void
|
setUlx(double value)
|
Thiết lập giá trị thuộc tính ulx
|
double
|
getUlx()
|
Trả về giá trị thuộc tính ulx.
|
void
|
setUly(double value)
|
Thiết lập giá trị thuộc tính uly
|
double
|
getUly()
|
Trả về giá trị thuộc tính uly.
|
void
|
setUrx(double value)
|
Thiết lập giá trị thuộc tính urx
|
double
|
getUrx()
|
Trả về giá trị thuộc tính urx
|
void
|
setUry(double value)
|
Thiết lập giá trị thuộc tính ury
|
double
|
getUry()
|
Trả về giá trị thuộc tính ury
|
void
|
setLlx(double value)
|
Thiết lập giá trị thuộc tính llx
|
double
|
getLlx()
|
Trả về giá trị thuộc tính llx
|
void
|
setLly(double value)
|
Thiết lập giá trị thuộc tính lly
|
double
|
getLly()
|
Trả về giá trị thuộc tính lly
|
void
|
setLrx(double value)
|
Thiết lập giá trị thuộc tính lrx
|
double
|
getLrx()
|
Trả về giá trị thuộc tính lrx
|
void
|
setLry(double value)
|
Thiết lập giá trị thuộc tính lry
|
double
|
getLry()
|
Trả về giá trị thuộc tính lry
|
d. Ví dụ :
Viết chương trình sử dụng Slider thể hiện giá trị góc quay khi quay input quanh 1 trục nhưng hình minh họa
Đầu tiên, chúng ta phải cung cấp tọa độ trục quay I1, I2 và gốc quay α. Dựa vào công thức lượng giác, chúng ta xác định tọa độ A’, B’, C’, D’.
xa’ = xi1 - (xi1 – xa) *cosα;
ya’ = yi1 - (xi1 – xa) * sinα;
xb’ = xi1 + (xi1 - xb) *cosα;
yb’ = yi1 - (xi1 – xb) * sinα;
xc’ = xi2 - (xi1 – xc) *cosα;
yc’ = yi2 + (xi1 – xc) * sinα;
xd’ = xi2 + (xi1 – xd) *cosα;
yd’ = yi2 - (xi1 – xd) * sinα;
Code :
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class examplePerspectiveTransform1 extends Application {
// tọa độ I1,I2
double xi1, yi1, xi2, yi2;
//góc xoay
double angle;
PerspectiveTransform perspectiveTrasform;
public void start(Stage primaryStage) {
Rectangle rectangle = new Rectangle(100, 50, 250, 200);
Image image = new Image("graphics/background.jpg");
rectangle.setFill(new ImagePattern(image));
// thiết lập tọa độ trục xoay ban đầu
xi1 = xi2 = rectangle.getX() + 100;
yi1 = rectangle.getY();
yi2 = rectangle.getY() + rectangle.getHeight();
//Slider thể hiện giá trị góc xoay
Label labelAngle = new Label("Góc xoay : ");
labelAngle.setLayoutX(0);
labelAngle.setLayoutY(300);
Slider sliderAngle = new Slider(0, 180, 10);
sliderAngle.setLayoutX(100);
sliderAngle.setLayoutY(300);
sliderAngle.setShowTickLabels(true);
sliderAngle.setShowTickMarks(true);
sliderAngle.setPrefWidth(300);
// Sự kiện khi thay đổi giá trị góc quay
sliderAngle.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
// thiết lập lại hiệu ứng PerspectiveTransform khi góc quay thay đổi
angle = Math.toRadians(newValue.doubleValue());
// tọa độ Ulx, Uly
double x1 = xi1 - (xi1 - rectangle.getX()) * Math.cos(angle);
double y1 = yi1 - (xi1 - rectangle.getX()) * Math.sin(angle);
// tọa độ Urx, Ury
double x2 = xi1 +
Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
double y2 = yi1 –
Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);
// tọa độ Llx, Lly
double x3 = xi2 - Math.abs(xi2 - (rectangle.getX())) * Math.cos(angle);
double y3 = yi2 + Math.abs(xi2 - (rectangle.getX())) * Math.sin(angle);
// tọa độ Lrx, Lry
double x4 = xi2 +
Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
double y4 = yi2 –
Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);
perspectiveTrasform.setUlx(x1);
perspectiveTrasform.setUly(y1);
perspectiveTrasform.setUrx(x2);
perspectiveTrasform.setUry(y2);
perspectiveTrasform.setLlx(x3);
perspectiveTrasform.setLly(y3);
perspectiveTrasform.setLrx(x4);
perspectiveTrasform.setLry(y4);
}
});
//Slider thể hiện giá trị tọa độ trục xoay I1,I2
Label labelPivot = new Label("Tọa độ trục xoay :");
labelPivot.setLayoutX(0);
labelPivot.setLayoutY(340);
Slider sliderPivot =
new Slider(rectangle.getX(), rectangle.getX() + rectangle.getWidth(), rectangle.getX());
sliderPivot.setShowTickLabels(true);
sliderPivot.setShowTickMarks(true);
sliderPivot.setLayoutX(100);
sliderPivot.setLayoutY(340);
sliderPivot.setPrefWidth(300);
// Sự kiện khi thay đổi tọa độ trục xoay
sliderPivot.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
// thiết lập lại tọa độ I1, I2
xi1 = xi2 = newValue.doubleValue();
}
});
// thiết lập sự hiển thị đầu tiên
angle = Math.toRadians(sliderAngle.getValue());
// tọa độ Ulx, Uly
double x1 = xi1 - (xi1 - rectangle.getX()) * Math.cos(angle);
double y1 = yi1 - (xi1 - rectangle.getX()) * Math.sin(angle);
// tọa độ Urx, Ury
double x2 =
xi1 + Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
double y2 =
yi1 - Math.abs(xi1 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);
// tọa độ Llx, Lly
double x3 = xi2 - Math.abs(xi2 - (rectangle.getX())) * Math.cos(angle);
double y3 = yi2 + Math.abs(xi2 - (rectangle.getX())) * Math.sin(angle);
// tọa độ Lrx, Lry
double x4 =
xi2 + Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.cos(angle);
double y4 =
yi2 - Math.abs(xi2 - (rectangle.getX() + rectangle.getWidth())) * Math.sin(angle);
// hiệu ứng PerspectiveTransform
perspectiveTrasform = new PerspectiveTransform();
perspectiveTrasform.setUlx(x1);
perspectiveTrasform.setUly(y1);
perspectiveTrasform.setUrx(x2);
perspectiveTrasform.setUry(y2);
perspectiveTrasform.setLlx(x3);
perspectiveTrasform.setLly(y3);
perspectiveTrasform.setLrx(x4);
perspectiveTrasform.setLry(y4);
rectangle.setEffect(perspectiveTrasform);
Group g = new Group();
g.setLayoutX(30);
g.setLayoutY(30);
g.getChildren().addAll(rectangle, sliderAngle, labelAngle, sliderPivot, labelPivot);
Scene scene = new Scene(g, 450, 400);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
|
12. DisplacementMap
DisplacementMap là hiệu ứng di chuyển các pixel trong input đặt vào vị trí khác trong output.
a.Các thuộc tính :
Kiểu
|
Tên thuộc tính
|
Giá trị mặc định
|
Miêu tả
|
Effect
|
Input
|
null
|
input của DisplacementMap
|
FloatMap
|
mapData
|
empty map
|
Xây dựng phương án xác định các input pixel sẽ được di chuyển vào output.
|
DoubleProperty
|
Offset
|
0.0
|
Khoảng cách cố định theo trục X được áp dụng đến tất cả các input pixel di chuyển vào output.
|
DoubleProperty
|
Offset
|
0.0
|
Khoảng cách cố định theo trục Y được áp dụng đến tất cả các input pixel được di chuyển vào output.
|
DoubleProperty
|
scaleX
|
1.0
|
Bội số được áp dụng đến mapData theo trục X
|
DoubleProperty
|
scaleY
|
1.0
|
Bội số được áp dụng đến mapData theo trục Y
|
BooleanProperty
|
Wrap
|
False
|
- Nếu wrap = false : các pixel nằm ngoài các cạnh biên không xuất hiện trong output.
- Nếu hệ số wrap = true, các pixel nằm ngoài đường biên được cuộn lại và xuất hiện trong output.
|
b. Cách sử dụng :
// Tạo đối tượng DisplacementMap với giá trị thuộc tính mặc định
DisplacementMap displacementMap = new DisplacementMap();
// Tạo đối tượng DisplacementMap cung cấp thuộc tính mapData
DisplacementMap displacementMap = new DisplacementMap(mapData);
// Tạo đối tượng DisplacementMap cung cấp mapData, offsetX, offsetY, scaleX, scaleY
DisplacementMap displacementMap =
new DisplacementMap(mapData, offsetX, offsetY, scaleX, scaleY);
|
c. Một số phương thức của DisplacementMap:
Kiểu trả về
|
Phương thức
|
Miêu tả
|
void
|
setInput(Effect value)
|
Cung cấp input cho DisplacementMap effect.
|
effect
|
getInput ()
|
Trả về input dành cho DisplacementMap.
|
void
|
setMapData(double value)
|
Thiết lập giá trị thuộc tính mapData.
|
floatmap
|
getMapData()
|
Trả về giá trị thuộc tính mapData.
|
void
|
setOffsetX(double value)
|
Thiết lập giá trị thuộc tính offsetX
|
double
|
getOffsetX()
|
Trả về giá trị thuộc tính offsetX.
|
void
|
setOffsetY(double value)
|
Thiết lập giá trị thuộc tính offsetY
|
double
|
getOffsetY()
|
Trả về giá trị thuộc tính offsetY .
|
void
|
setScaleX(double value)
|
Thiết lập giá trị thuộc tính scaleX.
|
double
|
getScaleX()
|
Trả về giá trị thuộc tính scaleX.
|
void
|
setScaleY(double value)
|
Thiết lập giá trị thuộc tính scaleY.
|
double
|
getScaleY()
|
Trả về giá trị thuộc tính scaleY .
|
boolean
|
isWrap()
|
Trả về giá trị thuộc tính wrap.
|
void
|
setWrap(boolean value)
|
Thiết lập giá trị thuộc tính wrap.
|
d. FloatMap class :
FloatMap là một cấu trúc dữ liệu mà mỗi phần tử trong nó được xem là 1 tọa độ điểm (x,y) và mỗi tọa độ chứa tối đa 4 giá trị kiểu float. Mỗi giá trị kiểu float của 1 tọa độ điểm được gọi là band. Do đó, mỗi tọa độ có 4 band và được đánh số thứ tự 0,1,2,3. Số phần tử trong FloatMap được xác định bởi width (chiều rộng) và height (chiều cao).
FloatMap thường được sử dụng cho pixel màu sắc. Bởi, pixel được xác định bằng tọa độ x,y và màu sắc được xác định bằng 4 thành phần red,green, blue, alpha.
Tạo đối tượng FloatMap:
// Tạo đối tượng FloatMap có widh và height mặc định
FloatMap fmap = new FloatMap();
// Tạo đối tượng FloatMap có widh = 10 và height 20
FloatMap fmap = new FloatMap(10,20);
|
Một số phương thức của FloatMap :
Kiểu trả về
|
Phương thức
|
Miêu tả
|
void
|
setWidth(int value)
|
Thiết lập width
|
int
|
getWidth()
|
Trả về giá trị width.
|
void
|
setHeight(int value)
|
Thiết lập height
|
int
|
getHeight()
|
Trả về giá trị height.
|
void
|
setSample(int x, int y, int band, float s)
|
Thiết lập giá trị s dành cho band nào tại vị trí x, y
|
void
|
setSample(int x, int y, float s0)
|
Thiết lập giá trị s0 dành cho band 0 tại vị trí x, y
|
void
|
setSample(int x, int y, float s0, float s1)
|
Thiết lập giá trị s0, s1 dành cho band 0 và 1 nào tại vị trí x, y
|
void
|
setSample(int x, int y, float s0, float s1, float s3, float s4)
|
Thiết lập giá trị s0, s1, s2, s3 dành cho band 0,1,2,3 nào tại vị trí x, y
|
e. Phương trình của DisplacementMap :
DisplacementMap tính toán các pixel được lấy từ input dựa trên phương trình :
dst[x,y] = src[ x + (offsetX + scaleX*map[x,y][0]) * srcWidth,
y + (offsetY + scaleY*map[x,y][1]) * srcHeight ]
trong đó :
- dst[x,y] : là pixel có tọa độ x,y trong output.
- src[x1,y1] : là pixel có tọa độ x1, y1 trong input. Với :
x1 = x + (offsetX + scaleX*map[x,y][0]) * srcWidth
y1 = y + (offsetY + scaleY*map[x,y][1]) * srcHeight
- offsetX, offsetY : tương ứng thuộc tính offset và offset.
- scaleX, scaleY: tương ứng thuộc tính scaleX, scaleY.
- map[x.y] : tương ứng thuộc tính mapData. Trong đó, map[x,y][0] , map[x,y][1] là giá trị kiểu float của band 0 và band 1 tại tọa độ x,y.
- srcWidth và srcHeight : là width và height của Input.
Ví dụ :
Giả sử offsetX = offsetY = 0, scaleX= scaleY = 1 , map[x,y][0] = 0.5, map[x,y][1] = -0.5, width = 2, height = 2. Phương trình trên có dạng như sau :
dst[x,y] = src[x + 1, y-1]
Để dễ hiểu, chúng tôi lập bảng thể hiện tọa độ pixel trong output được di chuyển từ pixel nào trong input :
Tọa độ pixel trong output
|
Tọa độ pixel trong input
|
0,0
|
1,-1
|
0,1
|
1,0
|
1,0
|
2,-1
|
1,1
|
2,0
|
Từ bảng trên, ta thấy :
- Pixel (1,-1) trong input được di chuyển vào pixel(0,0) của output. Nhưng trong input không tồn tại pixel (1,-1) bởi y < 0 nên tại pixel(0,0) của output không hiển thị gì cả.
- Pixel(1,0) trong input được di chuyển vào pixel(0,1) của output.
- Pixel (2,-1) trong input được di chuyển vào pixel(1,0) của output. Nhưng trong input không tồn tại pixel (2,-1) bởi y < 0 nên tại pixel(1,0) của output không hiển thị gì cả.
- Pixel (2,0) trong input được di chuyển vào pixel(1,1) của output. Nhưng trong input không tồn tại pixel (2,0) bởi x>=2 nên tại pixel(2,0) của output không hiển thị gì cả.
Kết quả, trong output chỉ hiển thị nội dung pixel(1,0) của input được di chuyển vào vị trí (0,1).
f. Thuộc tính offset :
Bạn muốn tất cả các pixel của input di chuyển 1 khoảng cách cố định so với các pixel trong output, bạn sử dụng thuộc tính offset. Thuộc tính offsetX và offsetY có giá trị [-1,1].
- Di chuyển về bên trái : giá trị offsetX > 0
- Di chuyển về bên phải : giá trị offsetX < 0
- Di chuyển hướng lên : giá trị offsetY > 0
- Di chuyển hướng xuống : giá trị offsetY < 0
Ví dụ : tạo 1 text = “DisplacementMap Example” và di chuyển text về bên phải 30%
Vị trí ban đầu
Sử dụng hiệu ứng DisplacementMap di chuyển về bên phải 30%
Code :
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.effect.DisplacementMap;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class DisplacementmapExample extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) {
// tạo hiệu ứng Displacement
DisplacementMap effect1 = new DisplacementMap();
// tất cả các pixel trong input đều di chuyển 30% về bên phải
effect1.setOffsetX(-0.3);
Text t1 = new Text("DisplacementMap Example");
t1.setFont(Font.font(36));
t1.setEffect(effect1);
HBox root = new HBox(t1);
root.setStyle("-fx-padding: 10;"
+ "-fx-border-style: solid inside;"
+ "-fx-border-width: 2;"
+ "-fx-border-insets: 5;"
+ "-fx-border-radius: 5;"
+ "-fx-border-color: blue;");
Scene scene = new Scene(root);
scene.setFill(Color.CORAL);
stage.setScene(scene);
stage.show();
}
}
|
g. Thuộc tính mapData
Thuộc tính mapData là thực thể của FloatMap class, mỗi phần tử trong mapData chứa tọa độ pixel (x,y), mỗi tọa độ cần 2 band : band 0 chứa giá trị di chuyển theo chiều ngang, band 1 chứa giá trị duy chuyển theo chiều dọc, band 3 và band 4 không dùng đến.
Sử dụng thuộc tính mapData để tính giá trị di chuyển theo 1 công thức nào đó như ý muốn của bạn. Ví dụ, di chuyển theo hàm sin, hàm cos, phân nửa trên di chuyển bên trái, phân nửa dưới di chuyển bên phải,…
Ví dụ : Sử dụng hiệu ứng DisplacementMap để di chuyển ảnh theo hàm cos
Ảnh ban đầu
Ảnh sử dụng hiệu ứng DisplacementMap theo hàm cos trên trục y
Code :
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.effect.DisplacementMap;
import javafx.scene.effect.FloatMap;
import javafx.scene.image.Image;
import javafx.scene.paint.ImagePattern;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class exampleDisplacementMap extends Application {
@Override
public void start(Stage primaryStage) {
Rectangle rectangle = new Rectangle(60, 20, 300, 200);
Image image = new Image("graphics/background.jpg");
rectangle.setFill(new ImagePattern(image));
// tạo đối tượng floatMap có width và height
// bằng width và height rectangle
FloatMap floatMap = new FloatMap();
floatMap.setWidth((int) rectangle.getWidth());
floatMap.setHeight((int) rectangle.getHeight());
// Vòng lặp for, thiết lập giá trị band 0 và band 1
// cho mỗi tọa độ x,y
for (int x = 0; x < rectangle.getWidth(); x++) {
// v chứa giá trị của 1 hàm cos theo tọa độ x
double v = Math.cos(x) / 50;
for (int y = 0; y < rectangle.getHeight(); y++) {
// thiết lập giá trị cho band 0 = 0.0f : không di chuyển theo chiều ngang
// band 1 = v : di chuyển theo chiều dọc khoảng v
floatMap.setSamples(x, y, 0.0f, (float) v);
}
}
DisplacementMap displacementMap = new DisplacementMap();
displacementMap.setMapData(floatMap);
rectangle.setEffect(displacementMap);
Group root = new Group();
root.getChildren().add(rectangle);
Scene scene = new Scene(root, 380, 300);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
|
h. Thuộc tính Wrap :
- Nếu wrap = false : các pixel nằm ngoài các cạnh biên không xuất hiện trong output.
- Nếu hệ số wrap = true : các pixel nằm ngoài đường biên được cuộn lại và xuất hiện trong output.
Vị trí ban đầu
Sử dụng hiệu ứng DisplacementMap di chuyển về bên phải 30% với wrap = false
Sử dụng hiệu ứng DisplacementMap di chuyển về bên phải 30% với wrap = true
k. Thuộc tính scale :
Thuộc tính scaleX và scaleY là bội số được dùng cho các giá trị của FloatMap. Bởi giá trị band 0 và band 1 của pixel trong mapData không được vượt quá [-1;1], nên chúng ta phải sử dụng thuộc tính scale để tăng giá trị của band.
Ảnh ban đầu
Ảnh sử dụng hiệu ứng DisplacementMap theo hàm cos trên y với scaleY = 1
Ảnh sử dụng hiệu ứng DisplacementMap theo hàm cos trên y với scaleY = 3
Không có nhận xét nào:
Đăng nhận xét