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