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

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

Added license headers to all files, added full GPL3 license file, moved license header generator script to dev/bin/scripts

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