source: trunk/src/org/expeditee/items/Picture.java@ 726

Last change on this file since 726 was 726, checked in by jts21, 10 years ago

Fix Picture.isCropped(), and change Picture rotation values to use degrees instead of radians

File size: 20.8 KB
Line 
1package org.expeditee.items;
2
3import java.awt.AlphaComposite;
4import java.awt.BasicStroke;
5import java.awt.Color;
6import java.awt.Graphics2D;
7import java.awt.Image;
8import java.awt.Point;
9import java.awt.Polygon;
10import java.awt.Rectangle;
11import java.awt.Shape;
12import java.awt.Stroke;
13import java.awt.Toolkit;
14import java.awt.geom.AffineTransform;
15import java.awt.geom.Point2D;
16import java.awt.image.BufferedImage;
17import java.awt.image.CropImageFilter;
18import java.awt.image.FilteredImageSource;
19import java.awt.image.ImageObserver;
20import java.io.File;
21import java.io.IOException;
22import java.text.DecimalFormat;
23
24import javax.imageio.ImageIO;
25import javax.swing.ImageIcon;
26
27import org.expeditee.gui.FrameGraphics;
28import org.expeditee.gui.FrameMouseActions;
29import org.expeditee.stats.Logger;
30
31/**
32 * This class represents an Image loaded from a file which is shown on the
33 * screen. Loading of the Image from disk occurs in the constructor, and takes
34 * approximately one second per mb of the Image file size. <br>
35 * <br>
36 * Currently Supported (Tested) Image formats:<br>
37 * BMP<br>
38 * JPG<br>
39 * GIF<br>
40 * GIF (Animated)<br>
41 * <br>
42 * Currently only the default size of the Image is supported, but future
43 * versions may support scaling.
44 *
45 * @author jdm18
46 *
47 */
48public class Picture extends XRayable {
49
50 private static final int MINIMUM_WIDTH = 10;
51
52 public static final int WIDTH = 0;
53
54 public static final int RATIO = 1;
55
56 protected Image _image = null;
57
58 private int _scaleType = RATIO;
59
60 private float _scale = 1.0f;
61
62 // Start of the crop relative to START
63 private Point _cropStart = null;
64
65 // Start of the crop relative to END
66 private Point _cropEnd = null;
67
68 private Point _start = new Point(0, 0);
69
70 private Point _end = new Point(0, 0);
71
72 private double _rotate = 0;
73
74 private boolean _flipX = false;
75 private boolean _flipY = false;
76
77 private boolean _showCropping = false;
78
79 private String _path = "";
80
81 private String _size = "";
82
83 private String _fileName = null;
84
85 // used to repaint animated GIF images, among other things.
86 protected ImageObserver _imageObserver = null;
87
88 protected Picture(Text source, ImageObserver observer, Image image) {
89 super(source);
90 _imageObserver = observer;
91 _image = image;
92
93 refresh();
94
95 if (_image != null)
96 parseSize();
97 }
98
99 /**
100 * Creates a new Picture from the given path. The ImageObserver is optional
101 * and can be set to NULL. <br>
102 * Note: It is assumed that the file described in path has already been
103 * checked to exist.
104 *
105 * @param source
106 * The Text Item that was used to create this Picture
107 * @param fileName
108 * the name of the file as it should be displayed in the source
109 * text
110 * @param path
111 * The Path of the Image to load from disk.
112 * @param observer
113 * The ImageObserver to assign when painting the Image on the
114 * screen.
115 */
116 public Picture(Text source, String fileName, String path, String size,
117 ImageObserver observer) {
118 super(source);
119 _imageObserver = observer;
120 _fileName = fileName;
121 _path = path;
122 _size = size;
123
124 refresh();
125 parseSize();
126 }
127
128 protected String getImageSize() {
129 return _size;
130 }
131
132 protected void parseSize() {
133 String size = getImageSize();
134
135 if (_end.x != 0 || _end.y != 0)
136 return;
137
138 // set the default values for start and end
139 _start.setLocation(0, 0);
140 if (_image == null)
141 _end.setLocation(0, 0);
142 else
143 _end.setLocation(_image.getWidth(null), _image.getHeight(null));
144 size = size.trim();
145 String sizeLower = size.toLowerCase();
146 String[] values = size.split("\\s+");
147 // Now get the cropping values if there are any
148 try {
149 if (values.length > 2) {
150 int startX = Integer.parseInt(values[1]);
151 int startY = Integer.parseInt(values[2]);
152 _start = new Point(startX, startY);
153 if (values.length > 4) {
154 int endX = Integer.parseInt(values[3]);
155 int endY = Integer.parseInt(values[4]);
156 _end = new Point(endX, endY);
157 }
158 scaleCrop();
159 }
160 } catch (Exception e) {
161 }
162
163 if(sizeLower.contains("flipx")) {
164 _flipX = true;
165 }
166
167 if(sizeLower.contains("flipy")) {
168 _flipY = true;
169 }
170
171 int index = sizeLower.indexOf("rotation=");
172 if(index != -1) {
173 int tmp = sizeLower.indexOf(" ", index);
174 String rotation;
175 if(tmp == -1) {
176 rotation = sizeLower.substring(index + "rotation=".length());
177 } else {
178 rotation = sizeLower.substring(index + "rotation=".length(), index + tmp);
179 }
180 _rotate = Double.parseDouble(rotation);
181 }
182
183 try {
184 if (size.length() == 0) {
185 size = "" + _image.getWidth(null);
186 _source.setText(getTagText() + size);
187 return;
188 }
189 size = values[0];
190 // parse width or ratio from text
191 if (size.contains(".")) {
192 // this is a ratio
193 _scale = Float.parseFloat(size);
194 _scaleType = RATIO;
195 } else if (size.length() > 0) {
196 // this is an absolute width
197 int width = Integer.parseInt(size);
198 _scaleType = WIDTH;
199 setWidth(width);
200 }
201 } catch (Exception e) {
202 _scale = 1F;
203 }
204 }
205
206 public void setStartCrop(int x, int y) {
207 invalidateCroppedArea();
208 _cropStart = new Point(x - getX(), y - getY());
209 invalidateCroppedArea();
210 }
211
212 public void setEndCrop(int x, int y) {
213 invalidateCroppedArea();
214 _cropEnd = new Point(x - getX(), y - getY());
215 invalidateCroppedArea();
216 }
217
218 private void invalidateCroppedArea() {
219 if (_cropStart != null && _cropEnd != null) {
220 Point topLeft = getTopLeftCrop();
221 Point bottomRight = getBottomRightCrop();
222 int startX = getX() + topLeft.x - _highlightThickness;
223 int startY = getY() + topLeft.y - _highlightThickness;
224 int border = 2 * _highlightThickness;
225 invalidate(new Rectangle(startX, startY, bottomRight.x - topLeft.x
226 + 2 * border, bottomRight.y - topLeft.y + 2 * border));
227 invalidateAll();
228 } else {
229 invalidateAll();
230 }
231 }
232
233 public Point getTopLeftCrop() {
234 return new Point(Math.min(_cropStart.x, _cropEnd.x), Math.min(
235 _cropStart.y, _cropEnd.y));
236 }
237
238 public Point getBottomRightCrop() {
239 return new Point(Math.max(_cropStart.x, _cropEnd.x), Math.max(
240 _cropStart.y, _cropEnd.y));
241 }
242
243 public void setShowCrop(boolean value) {
244 // invalidateCroppedArea();
245 _showCropping = value;
246 invalidateCroppedArea();
247 }
248
249 public boolean isCropTooSmall() {
250 if (_cropStart == null || _cropEnd == null)
251 return true;
252
253 int cropWidth = Math.abs(_cropEnd.x - _cropStart.x);
254 int cropHeight = Math.abs(_cropEnd.y - _cropStart.y);
255
256 return cropWidth < MINIMUM_WIDTH || cropHeight < MINIMUM_WIDTH;
257 }
258
259 public void clearCropping() {
260 invalidateCroppedArea();
261 _cropStart = null;
262 _cropEnd = null;
263 setShowCrop(false);
264 }
265
266 public void updatePolygon() {
267 if (_image == null) {
268 refresh();
269 parseSize();
270 }
271
272 Point[] ori = new Point[4];
273 Point2D[] rot = new Point2D[4];
274 Point centre = new Point();
275
276 if (_cropStart == null || _cropEnd == null) {
277 int width = getWidth();
278 int height = getHeight();
279
280 centre.x = _source.getX() + width / 2;
281 centre.y = _source.getY() + height / 2;
282
283 int xdiff = -MARGIN_RIGHT; // -getLeftMargin();
284
285 // extra pixel around the image so the highlighting is visible
286// _poly.addPoint(_source.getX() + 1 + xdiff, _source.getY() - 1);
287// _poly.addPoint(_source.getX() + width, _source.getY() - 1);
288// _poly.addPoint(_source.getX() + width, _source.getY() + height);
289// _poly.addPoint(_source.getX() + 1 + xdiff, _source.getY() + height);
290
291 ori[0] = new Point(_source.getX() + 1 + xdiff, _source.getY() - 1);
292 ori[1] = new Point(_source.getX() + width, _source.getY() - 1);
293 ori[2] = new Point(_source.getX() + width, _source.getY() + height);
294 ori[3] = new Point(_source.getX() + 1 + xdiff, _source.getY() + height);
295
296 } else {
297 Point topLeft = getTopLeftCrop();
298 Point bottomRight = getBottomRightCrop();
299
300 centre.x = _source.getX() + (bottomRight.x - topLeft.x) / 2;
301 centre.y = _source.getY() + (bottomRight.y - topLeft.y) / 2;
302
303 Rectangle clip = new Rectangle(topLeft.x + _source.getX(),
304 topLeft.y + _source.getY(), bottomRight.x - topLeft.x,
305 bottomRight.y - topLeft.y).getBounds();
306// _poly.addPoint((int) clip.getMinX() - 1, (int) clip.getMinY() - 1);
307// _poly.addPoint((int) clip.getMinX() - 1, (int) clip.getMaxY());
308// _poly.addPoint((int) clip.getMaxX(), (int) clip.getMaxY());
309// _poly.addPoint((int) clip.getMaxX(), (int) clip.getMinY() - 1);
310
311 ori[0] = new Point((int) clip.getMinX() - 1, (int) clip.getMinY() - 1);
312 ori[1] = new Point((int) clip.getMinX() - 1, (int) clip.getMaxY());
313 ori[2] = new Point((int) clip.getMaxX(), (int) clip.getMaxY());
314 ori[3] = new Point((int) clip.getMaxX(), (int) clip.getMinY() - 1);
315
316 }
317
318 AffineTransform.getRotateInstance(Math.PI * _rotate / 180, centre.x, centre.y).transform(ori, 0, rot, 0, 4);
319
320 _poly = new Polygon();
321 for(Point2D p : rot) {
322 _poly.addPoint((int)p.getX(), (int)p.getY());
323 }
324 }
325
326 @Override
327 public double getEnclosedArea() {
328 return getWidth() * getHeight();
329 }
330
331 @Override
332 public void setWidth(Integer width) {
333 _scale = width * 1F / (_end.x - _start.x);
334 }
335
336 public Point getStart() {
337 return _start;
338 }
339
340 public Point getEnd() {
341 return _end;
342 }
343
344 /**
345 * Gets the width with which the picture is displayed on the screen.
346 */
347 @Override
348 public Integer getWidth() {
349 return Math.round(getUnscaledWidth() * _scale);
350 }
351
352 /**
353 * Gets the height with which the picture is displayed on the screen.
354 */
355 @Override
356 public int getHeight() {
357 return Math.round(getUnscaledHeight() * _scale);
358 }
359
360 /**
361 * Dont paint links in audience mode for images.
362 */
363 @Override
364 protected void paintLink(Graphics2D g) {
365 if (FrameGraphics.isAudienceMode())
366 return;
367 super.paintLink(g);
368 }
369
370 public void paintImageTiling(Graphics2D g) {
371 if (_image == null) {
372 return;
373 }
374
375 int iw = _image.getWidth(null);
376 int ih = _image.getHeight(null);
377 if(iw <= 0 || ih <= 0) {
378 return;
379 }
380
381 int dX1 = _source.getX();
382 int dY1 = _source.getY();
383 int dX2 = _source.getX() + getWidth();
384 int dY2 = _source.getY() + getHeight();
385
386 BufferedImage tmp = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
387 Graphics2D g2d = tmp.createGraphics();
388 int offX = (tmp.getWidth() - getWidth()) / 2;
389 int offY = (tmp.getHeight() - getHeight()) / 2;
390
391 // g2d.rotate(rotate, tmp.getWidth() / 2, tmp.getHeight() / 2);
392
393 int cropStartX = _start.x;
394 int cropEndX = _end.x;
395 if(cropEndX > iw) {
396 cropEndX = iw;
397 }
398 for(int x = dX1; x < dX2; ) {
399 // end - start = (cropEnd - cropStart) * scale
400 // => cropEnd = cropStart + (end - start) / scale
401 int w = (int) ((cropEndX - cropStartX) * _scale);
402 int endX = x + w;
403 if(endX > dX2) {
404 endX = dX2;
405 cropEndX = cropStartX + (int) ((dX2 - x) / _scale);
406 }
407
408 int cropStartY = _start.y;
409 int cropEndY = _end.y;
410 if(cropEndY > ih) {
411 cropEndY = ih;
412 }
413 for(int y = dY1; y < dY2; ) {
414 int h = (int) ((cropEndY - cropStartY) * _scale);
415 int endY = y + h;
416 if(endY > dY2) {
417 endY = dY2;
418 cropEndY = cropStartY + (int) ((dY2 - y) / _scale);
419 }
420
421 int sx = _flipX ? cropEndX : cropStartX;
422 int ex = _flipX ? cropStartX : cropEndX;
423 int sy = _flipY ? cropEndY : cropStartY;
424 int ey = _flipY ? cropStartY : cropEndY;
425 g2d.drawImage(_image, x - dX1 + offX, y - dY1 + offY, endX - dX1 + offX, endY - dY1 + offY, sx, sy, ex, ey, null);
426
427 cropStartY = 0;
428 cropEndY = ih;
429
430 y = endY;
431 }
432
433 cropStartX = 0;
434 cropEndX = iw;
435
436 x = endX;
437 }
438
439 AffineTransform at = new AffineTransform();
440 at.translate(dX1, dY1);
441 at.rotate(Math.PI * _rotate / 180, tmp.getWidth() / 2, tmp.getHeight() / 2);
442 g.drawImage(tmp, at, _imageObserver);
443 // g.drawImage(tmp, dX1, dY1, dX2, dY2, 0, 0, tmp.getWidth(), tmp.getHeight(), _imageObserver);
444 }
445
446 @Override
447 public void paint(Graphics2D g) {
448 if (_image == null)
449 return;
450
451 paintLink(g);
452
453 // if we are showing the cropping, then show the original as transparent
454 if (_showCropping && !isCropTooSmall()) {
455 // show the full image as transparent
456 float alpha = .5f;
457 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
458 alpha));
459
460 paintImageTiling(g);
461
462 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
463 1.0f));
464 // show the cropped area normally
465 Point topLeft = getTopLeftCrop();
466 Point bottomRight = getBottomRightCrop();
467 Shape clip = new Rectangle(_source.getX() + topLeft.x, _source.getY() + topLeft.y,
468 bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
469 g.setColor(getPaintHighlightColor());
470 g.draw(clip);
471 g.setClip(clip);
472
473 paintImageTiling(g);
474
475 g.draw(clip);
476 // if the image is cropped, but we are not showing the cropping
477 // otherwise, paint normally
478 } else {
479 paintImageTiling(g);
480 }
481
482 if (hasVisibleBorder()) {
483 g.setColor(getPaintBorderColor());
484 Stroke borderStroke = new BasicStroke(getThickness(), CAP, JOIN);
485 g.setStroke(borderStroke);
486 g.drawPolygon(getPolygon());
487 }
488
489 if (isHighlighted()) {
490 Stroke borderStroke = new BasicStroke(1, CAP, JOIN);
491 g.setStroke(borderStroke);
492 g.setColor(getHighlightColor());
493 g.drawPolygon(getPolygon());
494 }
495
496 //System.out.print("p_");
497 }
498
499 @Override
500 public Color getHighlightColor() {
501 if (_highlightColor.equals(getBorderColor()))
502 return ALTERNATE_HIGHLIGHT;
503 return _highlightColor;
504 }
505
506 @Override
507 public int setHighlightColor() {
508 super.setHighlightColor();
509
510 return Item.DEFAULT_CURSOR;
511 }
512
513 protected Picture createPicture() {
514 return ItemUtils.CreatePicture((Text) _source.copy(), _imageObserver);
515 }
516
517 @Override
518 public Picture copy() {
519 Picture p = createPicture();
520 p._image = _image;
521 p._mode = _mode;
522 // Doing Duplicate item duplicates link mark which we dont want to do
523 // when in audience mode because the linkMark will be copied incorrectly
524 // Get all properties from the source
525
526 if (!isCropTooSmall() && _cropStart != null && _cropEnd != null) {
527 assert (_cropEnd != null);
528 // make the start be the top left
529 // make the end be the bottom right
530 Point topLeft = getTopLeftCrop();
531 Point bottomRight = getBottomRightCrop();
532 int startX = Math.round(topLeft.x / _scale) + _start.x;
533 int startY = Math.round(topLeft.y / _scale) + _start.y;
534 int endX = Math.round(bottomRight.x / _scale + _start.x);
535 int endY = Math.round(bottomRight.y / _scale + _start.y);
536 int width = _image.getWidth(null);
537 int height = _image.getHeight(null);
538 // adjust our start and end if the user has dragged outside of the
539 // shape
540 if (endX > width) {
541 endX = width;
542 }
543 if (endY > height) {
544 endY = height;
545 }
546 if (startX < 0) {
547 startX = 0;
548 }
549 if (startY < 0) {
550 startY = 0;
551 }
552 p._start = new Point(startX, startY);
553 p._end = new Point(endX, endY);
554 p._source.setPosition(topLeft.x + _source.getX(), topLeft.y
555 + _source.getY());
556 } else {
557 p._start = new Point(_start);
558 p._end = new Point(_end);
559 }
560 p._scale = _scale;
561 p._scaleType = _scaleType;
562 p._path = _path;
563 p._fileName = _fileName;
564
565 p.updateSource();
566 p.updatePolygon();
567
568 return p;
569 }
570
571 public float getScale() {
572 return _scale;
573 }
574
575 public void setScale(float scale) {
576 _scale = scale;
577 }
578
579 public void scaleCrop() {
580 // scale crop values to within image bounds
581 int iw = _image.getWidth(null);
582 int ih = _image.getHeight(null);
583 if(iw > 0 || ih > 0) {
584 while(_start.x >= iw) {
585 _start.x -= iw;
586 _end.x -= iw;
587 }
588 while(_start.y >= ih) {
589 _start.y -= ih;
590 _end.y -= ih;
591 }
592 while(_start.x < 0) {
593 _start.x += iw;
594 _end.x += iw;
595 }
596 while(_start.y < 0) {
597 _start.y += ih;
598 _end.y += ih;
599 }
600 }
601 }
602
603 public void setCrop(int startX, int startY, int endX, int endY) {
604 _start = new Point(startX, startY);
605 _end = new Point(endX, endY);
606 updateSource();
607 }
608
609 @Override
610 public float getSize() {
611 return _source.getSize();
612 }
613
614 @Override
615 public void setSize(float size) {
616 float diff = size - _source.getSize();
617 float oldScale = _scale;
618
619 float multiplier = (1000F + diff * 40F) / 1000F;
620 _scale = _scale * multiplier;
621
622 // picture must still be at least XX pixels wide
623 if (getWidth() < MINIMUM_WIDTH) {
624 _scale = oldScale;
625 } else {
626 _source.translate(new Point2D.Float(FrameMouseActions.MouseX,
627 FrameMouseActions.MouseY), multiplier);
628 }
629 updateSource();
630 updatePolygon();
631 // Make sure items that are resized display the border
632 invalidateAll();
633 }
634
635 @Override
636 public void setAnnotation(boolean val) {
637 }
638
639 /**
640 * Returns the Image that this Picture object is painting on the screen.
641 * This is used by Frame to repaint animated GIFs.
642 *
643 * @return The Image that this Picture object represents.
644 */
645 public Image getImage() {
646 return _image;
647 }
648
649 public Image getCroppedImage() {
650 if (_image == null)
651 return null;
652 if (!isCropped()) {
653 return _image;
654 }
655
656 return Toolkit.getDefaultToolkit().createImage(
657 new FilteredImageSource(_image.getSource(),
658 new CropImageFilter(_start.x, _start.y,
659 getUnscaledWidth(), getUnscaledHeight())));
660 }
661
662 public int getUnscaledWidth() {
663 return _end.x - _start.x;
664 }
665
666 public int getUnscaledHeight() {
667 return _end.y - _start.y;
668 }
669
670 /**
671 * @return true if this is a cropped image.
672 */
673 public boolean isCropped() {
674 return (_end.x != 0 && _end.x != _image.getWidth(null)) || (_end.y != 0 && _end.y != _image.getHeight(null)) || _start.y != 0 || _start.x != 0;
675 }
676
677 @Override
678 public boolean refresh() {
679 // ImageIcon is faster, but cannot handle some formats
680 // (notably.bmp) hence, we try this first, then if it fails we try
681 // ImageIO
682 try {
683 _image = new ImageIcon(_path).getImage();
684 } catch (Exception e) {
685 }
686
687 // if ImageIcon failed to read the image
688 if (_image == null || _image.getWidth(null) <= 0) {
689 try {
690 _image = ImageIO.read(new File(_path));
691 } catch (IOException e) {
692 // e.printStackTrace();
693 Logger.Log(e);
694 _image = null;
695 return false;
696 }
697 }
698 return true;
699 }
700
701 @Override
702 protected int getLinkYOffset() {
703 return getBoundsHeight() / 2;
704 }
705
706 @Override
707 public void setLinkMark(boolean state) {
708 // TODO use the more efficient invalidiate method
709 // The commented code below is not quite working
710 // if(!state)
711 // invalidateCommonTrait(ItemAppearence.LinkChanged);
712 _source.setLinkMark(state);
713 // if(state)
714 // invalidateCommonTrait(ItemAppearence.LinkChanged);
715 invalidateAll();
716 }
717
718 @Override
719 public void setActionMark(boolean state) {
720 // if (!state)
721 // invalidateCommonTrait(ItemAppearence.LinkChanged);
722 _source.setActionMark(state);
723 // if (state)
724 // invalidateCommonTrait(ItemAppearence.LinkChanged);
725 invalidateAll();
726 }
727
728 @Override
729 public boolean getLinkMark() {
730 return !FrameGraphics.isAudienceMode() && _source.getLinkMark();
731 }
732
733 @Override
734 public boolean getActionMark() {
735 return _source.getActionMark();
736 }
737
738 @Override
739 public String getName() {
740 return _fileName;
741 }
742
743 protected String getTagText() {
744 return "@i: " + _fileName + " ";
745 }
746
747 /**
748 * Updates the source text for this item to match the current size of the
749 * image.
750 *
751 */
752 private void updateSource() {
753 StringBuffer newText = new StringBuffer(getTagText());
754
755 switch (_scaleType) {
756 case (RATIO):
757 DecimalFormat format = new DecimalFormat("0.00");
758 newText.append(format.format(_scale));
759 break;
760 case (WIDTH):
761 newText.append(getWidth());
762 break;
763 }
764
765 scaleCrop();
766
767 // If the image is cropped add the position for the start and finish of
768 // the crop to the soure text
769 if (_start.x > 0 || _start.y > 0 || _end.x != _image.getWidth(null)
770 || _end.y != _image.getHeight(null)) {
771 newText.append(" ").append(_start.x).append(" ").append(_start.y);
772 newText.append(" ").append(_end.x).append(" ").append(_end.y);
773 }
774
775 if(_flipX) {
776 newText.append(" flipX");
777 }
778 if(_flipY) {
779 newText.append(" flipY");
780 }
781 if(Double.compare(_rotate, 0) != 0) {
782 newText.append(" rotation=" + _rotate);
783 }
784
785 _source.setText(newText.toString());
786 }
787
788 @Override
789 public void translate(Point2D origin, double ratio) {
790 _scale *= ratio;
791 updateSource();
792 super.translate(origin, ratio);
793 }
794
795 @Override
796 public Rectangle[] getDrawingArea() {
797
798 Rectangle[] da = super.getDrawingArea();
799
800 if (getLink() != null || hasAction()) {
801 Rectangle[] da2 = new Rectangle[da.length + 1];
802 System.arraycopy(da, 0, da2, 0, da.length);
803 da2[da.length] = getLinkPoly().getBounds();
804 da2[da.length].translate(getX() - LEFT_MARGIN, getY()
805 + getLinkYOffset());
806 da2[da.length].width += 2;
807 da2[da.length].height += 2;
808 da = da2;
809 }
810
811 return da;
812
813 }
814
815 @Override
816 public void scale(Float scale, int originX, int originY) {
817 setScale(getScale() * scale);
818 super.scale(scale, originX, originY);
819 }
820
821 public void setFlipX(boolean flip) {
822 _flipX = flip;
823 }
824
825 public void setFlipY(boolean flip) {
826 _flipY = flip;
827 }
828
829 public boolean getFlipX() {
830 return _flipX;
831 }
832
833 public boolean getFlipY() {
834 return _flipY;
835 }
836
837 public void setRotate(double rotate) {
838 _rotate = rotate;
839 updateSource();
840 updatePolygon();
841 }
842
843 public double getRotate() {
844 return _rotate;
845 }
846
847}
Note: See TracBrowser for help on using the repository browser.