Transformation
Transformation là khái niệm dùng để chỉ các phép biến đổi một hình ảnh này thành một hình ảnh khác, bên dưới thực chất nó là sự biến đổi hệ tọa độ, đưa hình ảnh từ hệ tọa độ này sang hệ tọa độ khác để hình này nhìn có vẻ khác đi. Bằng phương pháp toán học, người ta thực hiện phép chuyển đổi tọa độ bằng việc sử dụng ma trận. Mỗi phép chuyển đổi tọa độ – mỗi loại transformation như scaling (phóng to, thu nhỏ), reflection (tạo ảnh đối xứng), translation (dịch chuyển) và rotation (xoay) – đều có một ma trận đặc trưng cho nó. Bài viết sẽ giới thiệu các kiến thức toán học cơ bản về vector, ma trận và transform trong không gian 2D, đồng thời cũng giới thiệu về các công cụ mà WPF cung cấp để thao tác với các đối tượng toán học để tạo nên hiệu ứng 2D transformation cho UI control. WPF vẫn hỗ trợ transformation trong không gian 3D, nhưng sẽ không được đề cập đến vì khối lượng lý thuyết toán 3D khổng lồ khó có thể được trình bày trong một bài tìm hiểu ngắn.
Đại cương về Điểm, Vector, Ma trận và Transform
Vector và ma trận đóng vai trò quan trọng trong quá trình transformation. WPF định nghĩa điểm và vector là một mảng các con số (số lượng phần tử của điểm/vector là số chiều của không gian mà điểm/vector đang tồn tại) và ma trận là một mảng nhiều chiều.
![]() |
Trong không gian 2D, định nghĩa hai điểm A(x1, y1) và B(x2, y2) thì vector V1, V2 là các đoạn thẳng có hướng từ gốc tọa độ O đến các điểm A và B.Sự khác biệt giữa một điểm và một vector là điểm biểu diễn một vị trí cố định, trong khi đó vector biểu diễn phương chiều và độ lớn của một đoạn thẳng.
Trong toán học, V12 = V2 – V1, đây là vector biểu diễn đoạn thẳng có hướng từ A đến B. Trong WPF, đoạn code sau là hợp lệ: Vector v12 = new Point(x2, y2) – new Point(x1, y1); |
Để thực hiện transform một đối tượng nào đó trong không gian, ta lấy dạng biểu diễn toán học của đối tượng đó nhân cho ma trận đặc trưng. Ta có thể lấy điểm nhân cho ma trận để có một điểm mới, vector nhân cho ma trận để có một vector mới. Trong thực tế, để transform một hình ảnh bitmap, người ta convert hình đó thành các điểm và các vector rồi áp dụng quy luật trên. Thật may mắn trong WPF đã cài sẵn một bộ lọc cho phép convert raster image thành vector image, đồng thời các UI control đều được xây dựng bằng các nét vẽ vector nên quá trình transformation đã đơn giản hơn rất nhiều. Scaling
Để kéo giãn hay nén một đối tượng đồ họa theo chiều ngang, đơn giản ta lấy các vector và điểm nằm trong đối tượng đó nhân với một nhân tố sx. Dùng phương pháp tương tự ta cũng biến đổi kích thước của đối tượng theo chiều dọc với nhân tố sy. Quá trình scaling được mô tả bằng biểu thức sau:

Ví dụ ma trận dùng để scale transform chiều dài tăng lên gấp rưỡi và chiều cao giảm xuống một nửa được biểu diễn là . |
![]() |
Reflection
Để phản xạ một đối tượng theo trục x hoặc trục y, ta tạo ảnh qua gương của đối tượng đó. Phản xạ theo một trục cũng tương ứng với việc scale trục đó với một nhân tố âm:
- Ma trận dùng để phản xạ theo trục x:
. - Ma trận dùng để phản xạ theo trục y:
. - Ma trận có hai số -1 trên đường chéo chính là phép reflect cả hai chiều, đồng nghĩa với việc xoay đối tượng đó một góc 180o:

Rotation
Xét trường hợp muốn xoay đối tượng một góc θ theo chiều lượng giác. Giải quyết vấn đề này ở mức cơ bản nhất là xoay một điểm A(x1, y1) góc θ để tạo ra điểm A’(x2, y2).
|
|
Khoảng cách từ điểm ban đầu đến gốc tọa độ là r thì sau khi xoay khoảng cách này vẫn không được thay đổi. Ta có một số quan hệ giữa các đại lượng:
Sau khi xoay góc θ, tọa độ (x2, y2) được tính theo công thức: |
Do
và
nên cuối cùng ta có được công thức rút gọn của tọa độ mới như sau:
Dưới dạng ma trận, rotation transform nhận input là điểm (x1, y1) và góc xoay θ được biểu diễn như sau:
.
Lưu ý: Công thức này chỉ có thể xoay một hình xung quanh gốc tọa độ.
Translation
Để dịch chuyển một đối tượng, đơn giản công việc là thêm phần offset dx và dy vào thành phần X và Y của các điểm tạo nên đối tượng đó.
- x1 = x + dx
- y1 = y + dy
Mặc dù công thức trên rất đơn giản nhưng không thể định nghĩa được khái niệm translation transform bằng ma trận 2*2. Phải tìm ra một loại ma trận nào đó có thể biểu diễn được tất cả các loại transform cơ bản. Một kỹ thuật các nhà toán học thường dùng đó là nâng cấp ma trận, tức là tăng kích thước của ma trận mỗi chiều thêm một đơn vị. Phương pháp này được gọi là homogeneous coordinates, đã trở thành phương pháp chuẩn được sử dụng trong tất cả mọi thư viện đồ họa, kể cả WPF.
Homogeneous Coordinates
Trong homogeneous coordinates (môi trường tọa độ đồng nhất) 2D, tọa độ của một điểm bao gồm 3 thành phần (x, y, w) thay vì chỉ có (x, y) như trước đây. Nếu w ≠ 0, ta có thể chuẩn hóa bằng các chia tọa độ của điểm này cho w để có một tọa độ mới tương đương (x/w, y/w, 1), và các con số x/w, y/w được gọi là tọa độ điểm trong hệ thống homogeneous coordinates. Nếu w = 0, điểm này được xem như nằm ở ∞. Tọa độ điểm bao gồm 3 thành phần cũng đòi hỏi ma trận transform cũng phải có kích thức là 3*3.
- Scaling Transform:

- Rotation Transform:

- Translation Transform:

Combining Transforms
Trong thực hành, ít khi nào developer sử dụng duy nhất một kiểu transform, mà thường kết hợp nhiều loại transform lên một đối tượng để tạo ra hiệu ứng yêu thích. Ví dụ người ta muốn xoay một hình xung quanh một điểm ảnh P1 cho trước, không phải là xung quanh gốc tọa độ nữa. Đây là một kiểu transform phức tạp phải đi qua nhiều bước:
|
![]() |
Công thức để biểu diễn phép quay phức tạp trên:

Như vậy, thay vì lần lượt nhân cho các ma trận đơn giản, ta có thể tính toán trước ma trận của phép transform tổng hợp để chỉ cần áp đặt ma trận tổng hợp này vào đối tượng là có thể thực hiện được transformation phức tạp.
Transformation trong WPF
WPF cung cấp đầy đủ các class phục vụ cho việc transform, nắm được bản chất lý thuyết của transformation ở phần trên là cơ sở để ứng dụng và kết hợp các class này lại với nhau. Có 5 derived classes thông dụng nhất:
- ScaleTransform: scale đối tượng theo cả hai trục x và y. Thuộc tính ScaleX xác định tỷ lệ kéo dãn trục X ra bao nhiêu, tương tự với ScaleY. Thao tác scaling lấy điểm trung tâm được định nghĩa bởi thuộc tính CenterX và CenterY.
- TranslateTransform: Dịch chuyển đối tượng đi một khoảng cách được định nghĩa trong thuộc tính X và Y.
- RotateTransform: Xoay đối tượng trong không gian 2D một góc được định nghĩa trong thuộc tính Angle, lấy điểm xoay trung tâm là CenterX và CenterY.
- SkewTransform: Làm nghiêng đối tượng theo hai trục một góc được định nghĩa trong các thuộc tính AngleX và AngleY, sử dụng điểm trung tâm là (CenterX, CenterY).
- MatrixTransform: Nhận một ma trận kiểu TransformMatrix cho trước và áp ma trận này vào đối tượng đồ họa. Đây là phương pháp transformation tổng quát nhất, thường sử dụng cho những kiểu transform khác thường và khá phức tạp.
- TransformGroup: Có hiệu quả tương tự như MatrixTransform nhưng đối số nhận vào không phải là một ma trận mà là một danh sách các kiểu transform cơ bản. TransformGroup sẽ tự động tạo ra các ma trận của các phép cơ bản đó rồi nhân chúng lại với nhau, đưa về kiểu MatrixTransform.
MatrixTransform Class
TransformMatrix cũng gần giống với ma trận homogeneous được giới thiệu ở phần lý thuyết, nó chỉ bỏ đi cột cuối cùng do cột cuối luôn là một hằng số
. Tức là trong WPF, ma trận chỉ có 3 dòng và 2 cột, với 6 thuộc tính là M11, M12, M21, M22, OffsetX và OffsetY:
![]() |
==> | ![]() |
Việc sử dụng MatrixTransform có thể dùng XAML như sau:
<Button MinWidth=”100″>
Click
<Button.RenderTransform>
<MatrixTransform x:Name=”myMatrixTransform”>
<MatrixTransform.Matrix >
<!– OffsetX and OffsetY specify the position of the button, M11 stretches it, and M12 skews it. –>
<Matrix OffsetX=”10″ OffsetY=”100″ M11=”3″ M12=”2″/>
</MatrixTransform.Matrix>
</MatrixTransform>
</Button.RenderTransform>
</Button>

Hình ảnh của button chữ nhật thông thường đã được thay đổi
TransformGroup Class
Với ví dụ trên, thay vì sử dụng ma trận thì ta có thể điền trực tiếp các kiểu transform thành phần và gom chúng lại thành TransformGroup. Tác dụng của cách làm này là phân chia được vai trò của các transform thành viên để sau này có thể thay đổi dễ dàng.
<Button MinWidth=”100″>
Click
<Button.RenderTransform>
<TransformGroup>
<TranslateTransform X=”10″ Y=”100″ />
<ScaleTransform ScaleX=”3″ />
<SkewTransform AngleY=”33.33″ />
</TransformGroup>
</Button.RenderTransform>
</Button>
ScaleTransform Class
<StackPanel>
<Button Content=”None-Scaled Button” />
<Button Content=”X-Scaled Button”>
<Button.RenderTransform>
<ScaleTransform ScaleX=”0.5″ />
</Button.RenderTransform>
</Button>
<Button Content=”Y-Scaled Button”>
<Button.RenderTransform>
<ScaleTransform ScaleY=”3″ />
</Button.RenderTransform>
</Button>
</StackPanel>

3 button: bình thường; thu nhỏ một nửa theo chiều ngang; kéo giãn gấp 3 theo chiều dọc
TranslateTransform Class
<Grid>
<Button Width=”100″ Height=”40″ Content=”Main Button” />
<Button Width=”100″ Height=”40″ Content=”(+30, -50)”>
<Button.RenderTransform>
<TranslateTransform X=”30″ Y=”-50″ />
</Button.RenderTransform>
</Button>
<Button Width=”100″ Height=”40″ Content=”(-70, +60)”>
<Button.RenderTransform>
<TranslateTransform X=”-70″ Y=”60″ />
</Button.RenderTransform>
</Button>
<Button Width=”100″ Height=”40″ Content=”(+60, +80)”>
<Button.RenderTransform>
<TranslateTransform X=”60″ Y=”80″ />
</Button.RenderTransform>
</Button>
</Grid>

Các button đã được chuyển dời một khoảng cách
RotateTransform Class
<Rectangle Width=”80″ Height=”10″ Stroke=”Blue” Fill=”Yellow” />
<Rectangle Width=”80″ Height=”10″ Stroke=”Blue” Fill=”Yellow”>
<Rectangle.RenderTransform>
<RotateTransform Angle=”15″ />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Width=”80″ Height=”10″ Stroke=”Blue” Fill=”Yellow”>
<Rectangle.RenderTransform>
<RotateTransform Angle=”30″ />
</Rectangle.RenderTransform>
</Rectangle>
<Rectangle Width=”80″ Height=”10″ Stroke=”Blue” Fill=”Yellow”>
<Rectangle.RenderTransform>
<RotateTransform Angle=”45″ />
</Rectangle.RenderTransform>
</Rectangle>

Hình cây quạt được tạo thành từ 4 hình chữ nhật với góc xoay 0o, 15o, 30o, 45o
SkewTransform Class
<ListBox Margin=”51,71,107,91″>
<ListBox.RenderTransform>
<SkewTransform AngleX=”30″ AngleY=”15″ />
</ListBox.RenderTransform>
<ListBoxItem>Hanoi</ListBoxItem>
<ListBoxItem>Paris</ListBoxItem>
<ListBoxItem>Lion</ListBoxItem>
<ListBoxItem>New York</ListBoxItem>
<ListBoxItem>Moscow</ListBoxItem>
</ListBox>

Listbox đã được thêm vào SkewTransform effect


.










Rất cảm ơn những thông tin bổ ích tác giả đã cũng cấp. Tôi đang nghiêm cứu về WPF, đang gặp vướng mắc trong việc transform một đối tượng.
Cụ thể là tôi sử dụng drawingContext để vẽ ra một đối tượng.
Sau đó add các đối tượng vào một Canvas.
Sau khi transform đối tượng thì không thể control vị trí của đối tượng được.Cụ thể là sau khi transform việc move đối tượng làm tôi tốn nhiều thời gian mà chưa giải quyết được.
Có cách nào xác định vị trí của đối tượng sau khi transform một cách cụ thể mong tác giả giúp đỡ.
Xin chân thành cảm ơn.
Khi bạn add đối tượng vào trong Canvas thì hai thuộc tính Canvas.Left và Canvas.Top là tọa độ gốc của đối tượng đó. Default là bằng 0 nếu bạn không chỉ định.
Để xác định được vị trí hiện tại sau khi transform, thì tùy thuộc vào loại transform mà bạn cộng thêm vào tọa độ gốc một giá trị đại số nào đó. Ví dụ đối với TranslateTransform X=100 và Y=200 đi chẳng hạn, thì tọa độ thực lúc này sẽ là (Canvas.Left + X, Canvas.Top + Y).