1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.graphics;
15
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.internal.cocoa.*;
18
19 /**
20 * Instances of this class represent transformation matrices for
21 * points expressed as (x, y) pairs of floating point numbers.
22 * <p>
23 * Application code must explicitly invoke the <code>Transform.dispose()</code>
24 * method to release the operating system resources managed by each instance
25 * when those instances are no longer required.
26 * </p>
27 * <p>
28 * This class requires the operating system's advanced graphics subsystem
29 * which may not be available on some platforms.
30 * </p>
31 *
32 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: GraphicsExample</a>
33 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
34 *
35 * @since 3.1
36 */
37 public class Transform extends Resource {
38 /**
39 * the OS resource for the Transform
40 * (Warning: This field is platform dependent)
41 * <p>
42 * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
43 * public API. It is marked public only so that it can be shared
44 * within the packages provided by SWT. It is not available on all
45 * platforms and should never be accessed from application code.
46 * </p>
47 *
48 * @noreference This field is not intended to be referenced by clients.
49 */
50 public NSAffineTransform handle;
51
52 /**
53 * Constructs a new identity Transform.
54 * <p>
55 * This operation requires the operating system's advanced
56 * graphics subsystem which may not be available on some
57 * platforms.
58 * </p>
59 * <p>
60 * You must dispose the transform when it is no longer required.
61 * </p>
62 *
63 * @param device the device on which to allocate the Transform
64 *
65 * @exception IllegalArgumentException <ul>
66 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
67 * </ul>
68 * @exception SWTException <ul>
69 * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
70 * </ul>
71 * @exception SWTError <ul>
72 * <li>ERROR_NO_HANDLES if a handle for the Transform could not be obtained</li>
73 * </ul>
74 *
75 * @see #dispose()
76 */
Transform(Device device)77 public Transform (Device device) {
78 this(device, 1, 0, 0, 1, 0, 0);
79 }
80
81 /**
82 * Constructs a new Transform given an array of elements that represent the
83 * matrix that describes the transformation.
84 * <p>
85 * This operation requires the operating system's advanced
86 * graphics subsystem which may not be available on some
87 * platforms.
88 * </p>
89 * <p>
90 * You must dispose the transform when it is no longer required.
91 * </p>
92 *
93 * @param device the device on which to allocate the Transform
94 * @param elements an array of floats that describe the transformation matrix
95 *
96 * @exception IllegalArgumentException <ul>
97 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device, or the elements array is null</li>
98 * <li>ERROR_INVALID_ARGUMENT - if the elements array is too small to hold the matrix values</li>
99 * </ul>
100 * @exception SWTException <ul>
101 * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
102 * </ul>
103 * @exception SWTError <ul>
104 * <li>ERROR_NO_HANDLES if a handle for the Transform could not be obtained</li>
105 * </ul>
106 *
107 * @see #dispose()
108 */
Transform(Device device, float[] elements)109 public Transform(Device device, float[] elements) {
110 this (device, checkTransform(elements)[0], elements[1], elements[2], elements[3], elements[4], elements[5]);
111 }
112
113 /**
114 * Constructs a new Transform given all of the elements that represent the
115 * matrix that describes the transformation.
116 * <p>
117 * This operation requires the operating system's advanced
118 * graphics subsystem which may not be available on some
119 * platforms.
120 * </p>
121 * <p>
122 * You must dispose the transform when it is no longer required.
123 * </p>
124 *
125 * @param device the device on which to allocate the Transform
126 * @param m11 the first element of the first row of the matrix
127 * @param m12 the second element of the first row of the matrix
128 * @param m21 the first element of the second row of the matrix
129 * @param m22 the second element of the second row of the matrix
130 * @param dx the third element of the first row of the matrix
131 * @param dy the third element of the second row of the matrix
132 *
133 * @exception IllegalArgumentException <ul>
134 * <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
135 * </ul>
136 * @exception SWTException <ul>
137 * <li>ERROR_NO_GRAPHICS_LIBRARY - if advanced graphics are not available</li>
138 * </ul>
139 * @exception SWTError <ul>
140 * <li>ERROR_NO_HANDLES if a handle for the Transform could not be obtained</li>
141 * </ul>
142 *
143 * @see #dispose()
144 */
Transform(Device device, float m11, float m12, float m21, float m22, float dx, float dy)145 public Transform (Device device, float m11, float m12, float m21, float m22, float dx, float dy) {
146 super(device);
147 NSAutoreleasePool pool = null;
148 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
149 try {
150 handle = NSAffineTransform.transform();
151 if (handle == null) SWT.error(SWT.ERROR_NO_HANDLES);
152 handle.retain();
153 setElements(m11, m12, m21, m22, dx, dy);
154 init();
155 } finally {
156 if (pool != null) pool.release();
157 }
158 }
159
checkTransform(float[] elements)160 static float[] checkTransform(float[] elements) {
161 if (elements == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
162 if (elements.length < 6) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
163 return elements;
164 }
165
166 @Override
destroy()167 void destroy() {
168 handle.release();
169 handle = null;
170 }
171
172 /**
173 * Fills the parameter with the values of the transformation matrix
174 * that the receiver represents, in the order {m11, m12, m21, m22, dx, dy}.
175 *
176 * @param elements array to hold the matrix values
177 *
178 * @exception SWTException <ul>
179 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
180 * </ul>
181 * @exception IllegalArgumentException <ul>
182 * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
183 * <li>ERROR_INVALID_ARGUMENT - if the parameter is too small to hold the matrix values</li>
184 * </ul>
185 */
getElements(float[] elements)186 public void getElements(float[] elements) {
187 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
188 if (elements == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
189 if (elements.length < 6) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
190 NSAutoreleasePool pool = null;
191 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
192 try {
193 NSAffineTransformStruct struct = handle.transformStruct();
194 elements[0] = (float)struct.m11;
195 elements[1] = (float)struct.m12;
196 elements[2] = (float)struct.m21;
197 elements[3] = (float)struct.m22;
198 elements[4] = (float)struct.tX;
199 elements[5] = (float)struct.tY;
200 } finally {
201 if (pool != null) pool.release();
202 }
203 }
204
205 /**
206 * Modifies the receiver such that the matrix it represents becomes the
207 * identity matrix.
208 *
209 * @exception SWTException <ul>
210 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
211 * </ul>
212 *
213 * @since 3.4
214 */
identity()215 public void identity() {
216 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
217 NSAutoreleasePool pool = null;
218 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
219 try {
220 NSAffineTransformStruct struct = new NSAffineTransformStruct();
221 struct.m11 = 1;
222 struct.m22 = 1;
223 handle.setTransformStruct(struct);
224 } finally {
225 if (pool != null) pool.release();
226 }
227 }
228
229 /**
230 * Modifies the receiver such that the matrix it represents becomes
231 * the mathematical inverse of the matrix it previously represented.
232 *
233 * @exception SWTException <ul>
234 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
235 * <li>ERROR_CANNOT_INVERT_MATRIX - if the matrix is not invertible</li>
236 * </ul>
237 */
invert()238 public void invert() {
239 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
240 NSAutoreleasePool pool = null;
241 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
242 try {
243 NSAffineTransformStruct struct = handle.transformStruct();
244 if ((struct.m11 * struct.m22 - struct.m12 * struct.m21) == 0) {
245 SWT.error(SWT.ERROR_CANNOT_INVERT_MATRIX);
246 }
247 handle.invert();
248 } finally {
249 if (pool != null) pool.release();
250 }
251 }
252
253 /**
254 * Returns <code>true</code> if the Transform has been disposed,
255 * and <code>false</code> otherwise.
256 * <p>
257 * This method gets the dispose state for the Transform.
258 * When a Transform has been disposed, it is an error to
259 * invoke any other method (except {@link #dispose()}) using the Transform.
260 *
261 * @return <code>true</code> when the Transform is disposed, and <code>false</code> otherwise
262 */
263 @Override
isDisposed()264 public boolean isDisposed() {
265 return handle == null;
266 }
267
268 /**
269 * Returns <code>true</code> if the Transform represents the identity matrix
270 * and false otherwise.
271 *
272 * @return <code>true</code> if the receiver is an identity Transform, and <code>false</code> otherwise
273 */
isIdentity()274 public boolean isIdentity() {
275 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
276 NSAutoreleasePool pool = null;
277 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
278 try {
279 NSAffineTransformStruct struct = handle.transformStruct();
280 return struct.m11 == 1 && struct.m12 == 0 && struct.m21 == 0 && struct.m22 == 1 && struct.tX == 0 && struct.tY == 0;
281 } finally {
282 if (pool != null) pool.release();
283 }
284 }
285
286 /**
287 * Modifies the receiver such that the matrix it represents becomes the
288 * the result of multiplying the matrix it previously represented by the
289 * argument.
290 *
291 * @param matrix the matrix to multiply the receiver by
292 *
293 * @exception SWTException <ul>
294 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
295 * </ul>
296 * @exception IllegalArgumentException <ul>
297 * <li>ERROR_NULL_ARGUMENT - if the parameter is null</li>
298 * <li>ERROR_INVALID_ARGUMENT - if the parameter has been disposed</li>
299 * </ul>
300 */
multiply(Transform matrix)301 public void multiply(Transform matrix) {
302 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
303 if (matrix == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
304 if (matrix.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
305 NSAutoreleasePool pool = null;
306 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
307 try {
308 handle.prependTransform(matrix.handle);
309 } finally {
310 if (pool != null) pool.release();
311 }
312 }
313
314 /**
315 * Modifies the receiver so that it represents a transformation that is
316 * equivalent to its previous transformation rotated by the specified angle.
317 * The angle is specified in degrees and for the identity transform 0 degrees
318 * is at the 3 o'clock position. A positive value indicates a clockwise rotation
319 * while a negative value indicates a counter-clockwise rotation.
320 *
321 * @param angle the angle to rotate the transformation by
322 *
323 * @exception SWTException <ul>
324 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
325 * </ul>
326 */
rotate(float angle)327 public void rotate(float angle) {
328 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
329 NSAutoreleasePool pool = null;
330 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
331 try {
332 handle.rotateByDegrees(angle);
333 } finally {
334 if (pool != null) pool.release();
335 }
336 }
337
338 /**
339 * Modifies the receiver so that it represents a transformation that is
340 * equivalent to its previous transformation scaled by (scaleX, scaleY).
341 *
342 * @param scaleX the amount to scale in the X direction
343 * @param scaleY the amount to scale in the Y direction
344 *
345 * @exception SWTException <ul>
346 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
347 * </ul>
348 */
scale(float scaleX, float scaleY)349 public void scale(float scaleX, float scaleY) {
350 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
351 NSAutoreleasePool pool = null;
352 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
353 try {
354 handle.scaleXBy(scaleX, scaleY);
355 } finally {
356 if (pool != null) pool.release();
357 }
358 }
359
360 /**
361 * Modifies the receiver to represent a new transformation given all of
362 * the elements that represent the matrix that describes that transformation.
363 *
364 * @param m11 the first element of the first row of the matrix
365 * @param m12 the second element of the first row of the matrix
366 * @param m21 the first element of the second row of the matrix
367 * @param m22 the second element of the second row of the matrix
368 * @param dx the third element of the first row of the matrix
369 * @param dy the third element of the second row of the matrix
370 *
371 * @exception SWTException <ul>
372 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
373 * </ul>
374 */
setElements(float m11, float m12, float m21, float m22, float dx, float dy)375 public void setElements(float m11, float m12, float m21, float m22, float dx, float dy) {
376 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
377 NSAutoreleasePool pool = null;
378 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
379 try {
380 NSAffineTransformStruct struct = new NSAffineTransformStruct();
381 struct.m11 = m11;
382 struct.m12 = m12;
383 struct.m21 = m21;
384 struct.m22 = m22;
385 struct.tX = dx;
386 struct.tY = dy;
387 handle.setTransformStruct(struct);
388 } finally {
389 if (pool != null) pool.release();
390 }
391 }
392
393 /**
394 * Modifies the receiver so that it represents a transformation that is
395 * equivalent to its previous transformation sheared by (shearX, shearY).
396 *
397 * @param shearX the shear factor in the X direction
398 * @param shearY the shear factor in the Y direction
399 *
400 * @exception SWTException <ul>
401 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
402 * </ul>
403 *
404 * @since 3.4
405 */
shear(float shearX, float shearY)406 public void shear(float shearX, float shearY) {
407 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
408 NSAutoreleasePool pool = null;
409 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
410 try {
411 NSAffineTransformStruct struct = new NSAffineTransformStruct();
412 struct.m11 = 1;
413 struct.m12 = shearX;
414 struct.m21 = shearY;
415 struct.m22 = 1;
416 NSAffineTransform matrix = NSAffineTransform.transform();
417 matrix.setTransformStruct(struct);
418 handle.prependTransform(matrix);
419 } finally {
420 if (pool != null) pool.release();
421 }
422 }
423
424 /**
425 * Given an array containing points described by alternating x and y values,
426 * modify that array such that each point has been replaced with the result of
427 * applying the transformation represented by the receiver to that point.
428 *
429 * @param pointArray an array of alternating x and y values to be transformed
430 *
431 * @exception IllegalArgumentException <ul>
432 * <li>ERROR_NULL_ARGUMENT - if the point array is null</li>
433 * </ul>
434 * @exception SWTException <ul>
435 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
436 * </ul>
437 */
transform(float[] pointArray)438 public void transform(float[] pointArray) {
439 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
440 if (pointArray == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
441 NSAutoreleasePool pool = null;
442 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
443 try {
444 NSPoint point = new NSPoint();
445 int length = pointArray.length / 2;
446 for (int i = 0, j = 0; i < length; i++, j += 2) {
447 point.x = pointArray[j];
448 point.y = pointArray[j + 1];
449 point = handle.transformPoint(point);
450 pointArray[j] = (float)point.x;
451 pointArray[j + 1] = (float)point.y;
452 }
453 } finally {
454 if (pool != null) pool.release();
455 }
456 }
457
458 /**
459 * Modifies the receiver so that it represents a transformation that is
460 * equivalent to its previous transformation translated by (offsetX, offsetY).
461 *
462 * @param offsetX the distance to translate in the X direction
463 * @param offsetY the distance to translate in the Y direction
464 *
465 * @exception SWTException <ul>
466 * <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
467 * </ul>
468 */
translate(float offsetX, float offsetY)469 public void translate(float offsetX, float offsetY) {
470 if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
471 NSAutoreleasePool pool = null;
472 if (!NSThread.isMainThread()) pool = (NSAutoreleasePool) new NSAutoreleasePool().alloc().init();
473 try {
474 handle.translateXBy(offsetX, offsetY);
475 } finally {
476 if (pool != null) pool.release();
477 }
478 }
479
480 /**
481 * Returns a string containing a concise, human-readable
482 * description of the receiver.
483 *
484 * @return a string representation of the receiver
485 */
486 @Override
toString()487 public String toString() {
488 if (isDisposed()) return "Transform {*DISPOSED*}";
489 float[] elements = new float[6];
490 getElements(elements);
491 return "Transform {" + elements [0] + ", " + elements [1] + ", " +elements [2] + ", " +elements [3] + ", " +elements [4] + ", " +elements [5] + "}";
492 }
493
494 }
495