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

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

File size: 25.6 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.io.File;
22import java.io.IOException;
23import java.text.DecimalFormat;
24
25import org.apache.commons.cli.CommandLine;
26import org.apache.commons.cli.CommandLineParser;
27import org.apache.commons.cli.GnuParser;
28import org.apache.commons.cli.Options;
29import org.apache.commons.cli.ParseException;
30import org.expeditee.core.Clip;
31import org.expeditee.core.Colour;
32import org.expeditee.core.Dimension;
33import org.expeditee.core.EnforcedClipStack.EnforcedClipKey;
34import org.expeditee.core.Image;
35import org.expeditee.core.Point;
36import org.expeditee.core.Stroke;
37import org.expeditee.core.bounds.AxisAlignedBoxBounds;
38import org.expeditee.core.bounds.CombinationBoxBounds;
39import org.expeditee.core.bounds.PolygonBounds;
40import org.expeditee.gio.EcosystemManager;
41import org.expeditee.gio.GraphicsManager;
42import org.expeditee.gui.DisplayController;
43import org.expeditee.gui.FrameGraphics;
44import org.expeditee.gui.FrameIO;
45import org.expeditee.gui.FrameUtils;
46
47/**
48 * This class represents an Image loaded from a file which is shown on the
49 * screen. Loading of the Image from disk occurs in the constructor, and takes
50 * approximately one second per mb of the Image file size. <br>
51 * <br>
52 * Currently Supported (Tested) Image formats:<br>
53 * BMP<br>
54 * JPG<br>
55 * GIF<br>
56 * GIF (Animated)<br>
57 * <br>
58 * Currently only the default size of the Image is supported, but future
59 * versions may support scaling.
60 *
61 * @author jdm18
62 *
63 */
64public class Picture extends XRayable {
65
66 private static final float CROPPING_COMPOSITE_ALPHA = 0.5f;
67
68 private static final int MINIMUM_WIDTH = 10;
69
70 public static final int WIDTH = 0;
71
72 public static final int RATIO = 1;
73
74 protected Image _image = null;
75
76 private int _scaleType = RATIO;
77
78 private float _scale = 1.0f;
79
80 // Start of the crop relative to START
81 private Point _cropStart = null;
82
83 // Start of the crop relative to END
84 private Point _cropEnd = null;
85
86 private Point _start = new Point(0, 0);
87
88 private Point _end = new Point(0, 0);
89
90 private double _rotate = 0;
91
92 private boolean _flipX = false;
93 private boolean _flipY = false;
94
95 private boolean _showCropping = false;
96
97 protected Integer _anchorLeft = null;
98 protected Integer _anchorTop = null;
99
100 private String _path = "";
101
102 private String _size = "";
103
104 private String _fileName = null;
105
106 protected Picture(Text source, Image image) {
107 super(source);
108 _image = image;
109
110 refresh();
111
112 if (_image != null) {
113 // Should parsing for minus options also be done?
114 // To be honest, looking at the code, can't really see how _size can be anything but
115 // the empty string at this stage of calling the constructor, although it does have
116 // the 'side' effect of setting other things (such as _start, _end, and _scale)
117 parseSize();
118 }
119 }
120
121 /**
122 * Creates a new Picture from the given path. The ImageObserver is optional
123 * and can be set to NULL. <br>
124 * Note: It is assumed that the file described in path has already been
125 * checked to exist.
126 *
127 * @param source
128 * The Text Item that was used to create this Picture
129 * @param fileName
130 * the name of the file as it should be displayed in the source
131 * text
132 * @param path
133 * The Path of the Image to load from disk.
134 * @param observer
135 * The ImageObserver to assign when painting the Image on the
136 * screen.
137 */
138 public Picture(Text source, String fileName, String path, String size)
139 {
140 super(source);
141 _fileName = fileName;
142 _path = path;
143
144 String size_without_options = parseMinusOptions(size);
145 _size = size_without_options;
146
147 refresh();
148 parseSize();
149 }
150
151 protected String getImageSize() {
152 return _size;
153 }
154
155 protected String parseMinusOptions(String cmd_line) {
156
157 String[] tokens = Text.parseArgsApache(cmd_line);
158
159 // make anything starting with a '-' lowercase
160 for (int i=0; i<tokens.length; i++) {
161 if (tokens[i].startsWith("-")) {
162 tokens[i] = tokens[i].toLowerCase();
163 }
164 }
165
166 // create the command line parser
167 CommandLineParser parser = new GnuParser();
168
169 // create the Options
170 Options options = new Options();
171 options.addOption( "al", "anchorleft", true, "Anchor the vertical left-hand edge of the interactive widget to the value provided " );
172 options.addOption( "at", "anchortop", true, "Anchor the vertical top edge of the interactive widget to the value provided " );
173
174 CommandLine core_line;
175 try {
176 // parse the command line arguments
177 core_line = parser.parse( options, tokens );
178
179 // Update tokens to be version with the options removed
180 tokens = core_line.getArgs();
181
182 }
183 catch( ParseException exp ) {
184 System.err.println( "Unexpected exception:" + exp.getMessage() );
185 core_line = null;
186
187 }
188
189 // Apply any anchor values supplied
190
191 if(core_line.hasOption( "anchorleft" ) ) {
192 String al_str = core_line.getOptionValue( "anchorleft" );
193
194 _anchorLeft = Integer.parseInt(al_str);
195 }
196
197 if(core_line.hasOption( "anchortop" ) ) {
198 String at_str = core_line.getOptionValue( "anchortop" );
199
200 _anchorTop = Integer.parseInt(at_str);
201 }
202
203
204 return String.join(" ", tokens);
205 }
206 protected void parseSize() {
207 String size = getImageSize();
208
209 if (_end.x != 0 || _end.y != 0)
210 return;
211
212 // set the default values for start and end
213 _start.set(0, 0);
214 if (_image == null)
215 _end.set(0, 0);
216 else
217 _end.set(_image.getWidth(), _image.getHeight());
218 size = size.trim();
219 String sizeLower = size.toLowerCase();
220 String[] values = size.split("\\s+");
221 // Now get the cropping values if there are any
222 try {
223 if (values.length > 2) {
224 int startX = Integer.parseInt(values[1]);
225 int startY = Integer.parseInt(values[2]);
226 _start = new Point(startX, startY);
227 if (values.length > 4) {
228 int endX = Integer.parseInt(values[3]);
229 int endY = Integer.parseInt(values[4]);
230 _end = new Point(endX, endY);
231 }
232 scaleCrop();
233 }
234 } catch (Exception e) {
235 }
236
237 if(sizeLower.contains("flipx")) {
238 _flipX = true;
239 }
240
241 if(sizeLower.contains("flipy")) {
242 _flipY = true;
243 }
244
245 int index = sizeLower.indexOf("rotation=");
246 if(index != -1) {
247 int tmp = sizeLower.indexOf(" ", index);
248 String rotation;
249 if(tmp == -1) {
250 rotation = sizeLower.substring(index + "rotation=".length());
251 } else {
252 rotation = sizeLower.substring(index + "rotation=".length(), index + tmp);
253 }
254 _rotate = Double.parseDouble(rotation);
255 }
256
257 try {
258 if (size.length() == 0) {
259 size = "" + _image.getWidth();
260 _source.setText(getTagText() + size);
261 return;
262 }
263 size = values[0];
264 // parse width or ratio from text
265 if (size.contains(".")) {
266 // this is a ratio
267 _scale = Float.parseFloat(size);
268 _scaleType = RATIO;
269 } else if (size.length() > 0) {
270 // this is an absolute width
271 int width = Integer.parseInt(size);
272 _scaleType = WIDTH;
273 setWidth(width);
274 }
275 } catch (Exception e) {
276 _scale = 1F;
277 }
278 }
279
280 public void setStartCrop(Point p)
281 {
282 if (p != null) setStartCrop(p.x, p.y);
283 }
284
285 public void setStartCrop(int x, int y) {
286 invalidateCroppedArea();
287 _cropStart = new Point(x - getX(), y - getY());
288 invalidateCroppedArea();
289 }
290
291 public void setEndCrop(Point p)
292 {
293 if (p != null) setEndCrop(p.x, p.y);
294 }
295
296 public void setEndCrop(int x, int y) {
297 invalidateCroppedArea();
298 _cropEnd = new Point(x - getX(), y - getY());
299 invalidateCroppedArea();
300 }
301
302 private void invalidateCroppedArea() {
303 if (_cropStart != null && _cropEnd != null) {
304 Point topLeft = getTopLeftCrop();
305 Point bottomRight = getBottomRightCrop();
306 int startX = getX() + topLeft.x - _highlightThickness;
307 int startY = getY() + topLeft.y - _highlightThickness;
308 int border = 2 * _highlightThickness;
309 // TODO: Why invalidate specific area just before invalidateAll? cts16
310 invalidate(new AxisAlignedBoxBounds(startX, startY,
311 bottomRight.x - topLeft.x + 2 * border, bottomRight.y - topLeft.y + 2 * border));
312 invalidateAll();
313 } else {
314 invalidateAll();
315 }
316 }
317
318 public Point getTopLeftCrop() {
319 return new Point(Math.min(_cropStart.x, _cropEnd.x), Math.min(
320 _cropStart.y, _cropEnd.y));
321 }
322
323 public Point getBottomRightCrop()
324 {
325 return new Point(Math.max(_cropStart.x, _cropEnd.x), Math.max(_cropStart.y, _cropEnd.y));
326 }
327
328 public void setShowCrop(boolean value) {
329 // invalidateCroppedArea();
330 _showCropping = value;
331 invalidateCroppedArea();
332 }
333
334 public boolean isBeingCropped()
335 {
336 return (_cropStart != null && _cropEnd != null);
337 }
338
339 public boolean isCropTooSmall()
340 {
341 if (!isBeingCropped()) return true;
342
343 int cropWidth = Math.abs(_cropEnd.x - _cropStart.x);
344 int cropHeight = Math.abs(_cropEnd.y - _cropStart.y);
345
346 return cropWidth < MINIMUM_WIDTH || cropHeight < MINIMUM_WIDTH;
347 }
348
349 public void clearCropping() {
350 invalidateCroppedArea();
351 _cropStart = null;
352 _cropEnd = null;
353 setShowCrop(false);
354 }
355
356 public PolygonBounds updateBounds()
357 {
358 if (_image == null) {
359 refresh();
360 parseSize();
361 }
362
363 Point[] ori = new Point[4];
364 Point centre = new Point();
365
366 int base_x = (_anchorLeft!=null) ? _anchorLeft : _source.getX();
367 int base_y = (_anchorTop!=null) ? _anchorTop : _source.getY();
368
369 if (_cropStart == null || _cropEnd == null) {
370 int width = getWidth();
371 int height = getHeight();
372
373 centre.x = base_x + width / 2;
374 centre.y = base_y + height / 2;
375
376 int xdiff = -MARGIN_RIGHT; // -getLeftMargin();
377
378 // extra pixel around the image so the highlighting is visible
379// _poly.addPoint(_source.getX() + 1 + xdiff, _source.getY() - 1);
380// _poly.addPoint(_source.getX() + width, _source.getY() - 1);
381// _poly.addPoint(_source.getX() + width, _source.getY() + height);
382// _poly.addPoint(_source.getX() + 1 + xdiff, _source.getY() + height);
383
384 ori[0] = new Point(base_x + 1 + xdiff, base_y - 1);
385 ori[1] = new Point(base_x + width, base_y - 1);
386 ori[2] = new Point(base_x + width, base_y + height);
387 ori[3] = new Point(base_x + 1 + xdiff, base_y + height);
388
389 } else {
390 Point topLeft = getTopLeftCrop();
391 Point bottomRight = getBottomRightCrop();
392
393 centre.x = base_x + (bottomRight.x - topLeft.x) / 2;
394 centre.y = base_y + (bottomRight.y - topLeft.y) / 2;
395
396 AxisAlignedBoxBounds clip = new AxisAlignedBoxBounds(topLeft.x + base_x,
397 topLeft.y + base_y, bottomRight.x - topLeft.x,
398 bottomRight.y - topLeft.y);
399// _poly.addPoint((int) clip.getMinX() - 1, (int) clip.getMinY() - 1);
400// _poly.addPoint((int) clip.getMinX() - 1, (int) clip.getMaxY());
401// _poly.addPoint((int) clip.getMaxX(), (int) clip.getMaxY());
402// _poly.addPoint((int) clip.getMaxX(), (int) clip.getMinY() - 1);
403
404 ori[0] = new Point((int) clip.getMinX() - 1, (int) clip.getMinY() - 1);
405 ori[1] = new Point((int) clip.getMinX() - 1, (int) clip.getMaxY());
406 ori[2] = new Point((int) clip.getMaxX(), (int) clip.getMaxY());
407 ori[3] = new Point((int) clip.getMaxX(), (int) clip.getMinY() - 1);
408
409 }
410
411 PolygonBounds poly = new PolygonBounds();
412 for (Point p : ori) {
413 poly.addPoint(p);
414 }
415 poly.rotate(Math.PI * _rotate / 180, centre);
416
417 return poly.close();
418 }
419
420 @Override
421 public double getEnclosedArea() {
422 return getWidth() * getHeight();
423 }
424
425 @Override
426 public void setWidth(Integer width) {
427 _scale = width * 1F / (_end.x - _start.x);
428 }
429
430 public Point getStart() {
431 return _start;
432 }
433
434 public Point getEnd() {
435 return _end;
436 }
437
438 /**
439 * Gets the width with which the picture is displayed on the screen.
440 */
441 @Override
442 public Integer getWidth() {
443 return Math.round(getUnscaledWidth() * _scale);
444 }
445
446 /**
447 * Gets the height with which the picture is displayed on the screen.
448 */
449 @Override
450 public int getHeight() {
451 return Math.round(getUnscaledHeight() * _scale);
452 }
453
454 /**
455 * Dont paint links in audience mode for images.
456 */
457 @Override
458 protected void paintLink()
459 {
460 if (DisplayController.isAudienceMode()) return;
461 super.paintLink();
462 }
463
464 /**
465 * Paint the image repeatedly tiled over the drawing area.
466 */
467 public void paintImageTiling()
468 {
469 if (_image == null) return;
470
471 int iw = _image.getWidth();
472 int ih = _image.getHeight();
473 if(iw <= 0 || ih <= 0) return;
474
475 int base_x = (_anchorLeft != null) ? _anchorLeft : _source.getX();
476 int base_y = (_anchorTop != null) ? _anchorTop : _source.getY();
477
478 int dX1 = base_x;
479 int dY1 = base_y;
480 int dX2 = base_x + getWidth();
481 int dY2 = base_y + getHeight();
482
483 Image tmp = Image.createImage(getWidth(), getHeight());
484 EcosystemManager.getGraphicsManager().pushDrawingSurface(tmp);
485
486 int offX = (tmp.getWidth() - getWidth()) / 2;
487 int offY = (tmp.getHeight() - getHeight()) / 2;
488
489 int cropStartX = _start.x;
490 int cropEndX = _end.x;
491 if(cropEndX > iw) {
492 cropEndX = iw;
493 }
494
495 for(int x = dX1; x < dX2; ) {
496 // end - start = (cropEnd - cropStart) * scale
497 // => cropEnd = cropStart + (end - start) / scale
498 int w = (int) ((cropEndX - cropStartX) * _scale);
499 int endX = x + w;
500 if(endX > dX2) {
501 endX = dX2;
502 cropEndX = cropStartX + (int) ((dX2 - x) / _scale);
503 }
504
505 int cropStartY = _start.y;
506 int cropEndY = _end.y;
507 if(cropEndY > ih) {
508 cropEndY = ih;
509 }
510
511 for(int y = dY1; y < dY2; ) {
512 int h = (int) ((cropEndY - cropStartY) * _scale);
513 int endY = y + h;
514 if(endY > dY2) {
515 endY = dY2;
516 cropEndY = cropStartY + (int) ((dY2 - y) / _scale);
517 }
518
519 int sx = _flipX ? cropEndX : cropStartX;
520 int ex = _flipX ? cropStartX : cropEndX;
521 int sy = _flipY ? cropEndY : cropStartY;
522 int ey = _flipY ? cropStartY : cropEndY;
523
524 Point topLeft = new Point(x - dX1 + offX, y - dY1 + offY);
525 Dimension size = new Dimension(endX - x, endY - y);
526 Point cropTopLeft = new Point(sx, sy);
527 Dimension cropSize = new Dimension(ex - sx, ey - sy);
528 if (cropSize.width > 0 && cropSize.height > 0) {
529 EcosystemManager.getGraphicsManager().drawImage(_image, topLeft, size, 0.0, cropTopLeft, cropSize);
530 }
531
532 cropStartY = 0;
533 cropEndY = ih;
534
535 y = endY;
536 }
537
538 cropStartX = 0;
539 cropEndX = iw;
540
541 x = endX;
542 }
543
544 EcosystemManager.getGraphicsManager().popDrawingSurface();
545 EcosystemManager.getGraphicsManager().drawImage(tmp, new Point(dX1, dY1), null, Math.PI * _rotate / 180);
546 tmp.releaseImage();
547 }
548
549 @Override
550 public void paint()
551 {
552 if (_image == null) return;
553
554 paintLink();
555
556 GraphicsManager g = EcosystemManager.getGraphicsManager();
557
558 // if we are showing the cropping
559 if (_showCropping && !isCropTooSmall()) {
560 // show the uncropped area as transparent
561 g.setCompositeAlpha(CROPPING_COMPOSITE_ALPHA);
562 paintImageTiling();
563 g.setCompositeAlpha(1.0f);
564
565 // show the cropped area normally
566 Point topLeft = getTopLeftCrop();
567 Point bottomRight = getBottomRightCrop();
568 int base_x = (_anchorLeft != null) ? _anchorLeft : _source.getX();
569 int base_y = (_anchorTop != null) ? _anchorTop : _source.getY();
570
571 Clip clip = new Clip(new AxisAlignedBoxBounds( base_x + topLeft.x,
572 base_y + topLeft.y,
573 bottomRight.x - topLeft.x,
574 bottomRight.y - topLeft.y));
575 EnforcedClipKey key = g.pushClip(clip);
576 paintImageTiling();
577 g.popClip(key);
578
579 // Draw an outline for the crop selection box
580 g.drawRectangle(clip.getBounds(), 0.0, null, getPaintHighlightColor(), HIGHLIGHT_STROKE, null);
581
582 // otherwise, paint normally
583 } else {
584 paintImageTiling();
585 }
586
587 PolygonBounds poly = (PolygonBounds) getBounds();
588
589 if (hasVisibleBorder()) {
590 Stroke borderStroke = new Stroke(getThickness(), DEFAULT_CAP, DEFAULT_JOIN);
591 g.drawPolygon(poly, null, null, 0.0, null, getPaintBorderColor(), borderStroke);
592 }
593
594 if (isHighlighted()) {
595 Stroke borderStroke = new Stroke(1, DEFAULT_CAP, DEFAULT_JOIN);
596 g.drawPolygon(poly, null, null, 0.0, null, getHighlightColor(), borderStroke);
597 }
598 }
599
600 @Override
601 public Colour getHighlightColor()
602 {
603 if (_highlightColour.equals(getBorderColor())) return ALTERNATE_HIGHLIGHT;
604 return _highlightColour;
605 }
606
607 protected Picture createPicture()
608 {
609 return ItemUtils.CreatePicture((Text) _source.copy());
610 }
611
612 @Override
613 public Picture copy() {
614 Picture p = createPicture();
615 p._image = _image;
616 p._highlightMode = _highlightMode;
617 // Doing Duplicate item duplicates link mark which we dont want to do
618 // when in audience mode because the linkMark will be copied incorrectly
619 // Get all properties from the source
620
621 if (!isCropTooSmall() && _cropStart != null && _cropEnd != null) {
622 assert (_cropEnd != null);
623 // make the start be the top left
624 // make the end be the bottom right
625 Point topLeft = getTopLeftCrop();
626 Point bottomRight = getBottomRightCrop();
627 int startX = Math.round(topLeft.x / _scale) + _start.x;
628 int startY = Math.round(topLeft.y / _scale) + _start.y;
629 int endX = Math.round(bottomRight.x / _scale + _start.x);
630 int endY = Math.round(bottomRight.y / _scale + _start.y);
631 int width = _image.getWidth();
632 int height = _image.getHeight();
633 // adjust our start and end if the user has dragged outside of the
634 // shape
635 if (endX > width) {
636 endX = width;
637 }
638 if (endY > height) {
639 endY = height;
640 }
641 if (startX < 0) {
642 startX = 0;
643 }
644 if (startY < 0) {
645 startY = 0;
646 }
647 p._start = new Point(startX, startY);
648 p._end = new Point(endX, endY);
649 int base_x = (_anchorLeft!=null) ? _anchorLeft : _source.getX();
650 int base_y = (_anchorTop!=null) ? _anchorTop : _source.getY();
651 p._source.setPosition(topLeft.x + base_x, topLeft.y + base_y);
652 } else {
653 p._start = new Point(_start);
654 p._end = new Point(_end);
655 }
656 p._scale = _scale;
657 p._scaleType = _scaleType;
658 p._path = _path;
659 p._fileName = _fileName;
660
661 p.updateSource();
662 p.invalidateBounds();
663
664 return p;
665 }
666
667 public float getScale() {
668 return _scale;
669 }
670
671 public void setScale(float scale) {
672 _scale = scale;
673 }
674
675 public void scaleCrop() {
676 // scale crop values to within image bounds
677 int iw = _image.getWidth();
678 int ih = _image.getHeight();
679 if(iw > 0 || ih > 0) {
680 while(_start.x >= iw) {
681 _start.x -= iw;
682 _end.x -= iw;
683 }
684 while(_start.y >= ih) {
685 _start.y -= ih;
686 _end.y -= ih;
687 }
688 while(_start.x < 0) {
689 _start.x += iw;
690 _end.x += iw;
691 }
692 while(_start.y < 0) {
693 _start.y += ih;
694 _end.y += ih;
695 }
696 }
697 }
698
699 public void setCrop(int startX, int startY, int endX, int endY) {
700 _start = new Point(startX, startY);
701 _end = new Point(endX, endY);
702 updateSource();
703 }
704
705 @Override
706 public float getSize() {
707 return _source.getSize();
708 }
709
710 @Override
711 public void setSize(float size) {
712 float diff = size - _source.getSize();
713 float oldScale = _scale;
714
715 float multiplier = (1000F + diff * 40F) / 1000F;
716 _scale = _scale * multiplier;
717
718 // picture must still be at least XX pixels wide
719 if (getWidth() < MINIMUM_WIDTH) {
720 _scale = oldScale;
721 } else {
722 _source.translate(EcosystemManager.getInputManager().getCursorPosition(), multiplier);
723 }
724 updateSource();
725 invalidateBounds();
726 // Make sure items that are resized display the border
727 invalidateAll();
728 }
729
730 @Override
731 public void setAnnotation(boolean val) {
732 }
733
734 /**
735 * Returns the Image that this Picture object is painting on the screen.
736 * This is used by Frame to repaint animated GIFs.
737 *
738 * @return The Image that this Picture object represents.
739 */
740 public Image getImage() {
741 return _image;
742 }
743
744 public Image getCroppedImage() {
745 if (_image == null)
746 return null;
747 if (!isCropped()) {
748 return _image;
749 }
750
751 return Image.createImageAsCroppedCopy(_image, _start.x, _start.y, getUnscaledWidth(), getUnscaledHeight());
752 }
753
754 public int getUnscaledWidth() {
755 return _end.x - _start.x;
756 }
757
758 public int getUnscaledHeight() {
759 return _end.y - _start.y;
760 }
761
762 /**
763 * @return true if this is a cropped image.
764 */
765 public boolean isCropped() {
766 return (_end.x != 0 && _end.x != _image.getWidth()) || (_end.y != 0 && _end.y != _image.getHeight()) || _start.y != 0 || _start.x != 0;
767 }
768
769 @Override
770 public boolean refresh() {
771 // ImageIcon is faster, but cannot handle some formats
772 // (notably.bmp) hence, we try this first, then if it fails we try
773 // ImageIO
774 /*
775 try {
776 _image = new ImageIcon(_path).getImage();
777 } catch (Exception e) {
778 }
779
780 // if ImageIcon failed to read the image
781 if (_image == null || _image.getWidth() <= 0) {
782 try {
783 _image = ImageIO.read(new File(_path));
784 } catch (IOException e) {
785 // e.printStackTrace();
786 Logger.Log(e);
787 _image = null;
788 return false;
789 }
790 }
791*/
792 _image = Image.getImage(_path);
793 return true;
794 }
795
796 @Override
797 protected int getLinkYOffset() {
798 return getBoundsHeight() / 2;
799 }
800
801 @Override
802 public void setLinkMark(boolean state) {
803 // TODO use the more efficient invalidiate method
804 // The commented code below is not quite working
805 // if(!state)
806 // invalidateCommonTrait(ItemAppearence.LinkChanged);
807 _source.setLinkMark(state);
808 // if(state)
809 // invalidateCommonTrait(ItemAppearence.LinkChanged);
810 invalidateAll();
811 }
812
813 @Override
814 public void setActionMark(boolean state) {
815 // if (!state)
816 // invalidateCommonTrait(ItemAppearence.LinkChanged);
817 _source.setActionMark(state);
818 // if (state)
819 // invalidateCommonTrait(ItemAppearence.LinkChanged);
820 invalidateAll();
821 }
822
823 @Override
824 public boolean getLinkMark() {
825 return !DisplayController.isAudienceMode() && _source.getLinkMark();
826 }
827
828 @Override
829 public boolean getActionMark() {
830 return _source.getActionMark();
831 }
832
833 @Override
834 public String getName() {
835 return _fileName;
836 }
837
838 public String getPath() {
839 return _path;
840 }
841
842 /**
843 * Copies the image to the default images folder and updates the reference to it in Expeditee
844 * Used for correcting image references for FrameShare
845 */
846 public void moveToImagesFolder() {
847 File f = new File(getPath());
848 // if the file is not in the default images folder, copy it there
849 if(! f.getParentFile().equals(new File(FrameIO.IMAGES_PATH))) {
850 try {
851 File f2 = new File(FrameIO.IMAGES_PATH + f.getName());
852 FrameUtils.copyFile(f, f2, false);
853 f = f2;
854 } catch (IOException e) {
855 e.printStackTrace();
856 f = null;
857 }
858 }
859 _path = f.getPath();
860 _fileName = f.getName();
861 updateSource();
862 }
863
864 protected String getTagText() {
865 return "@i: " + _fileName + " ";
866 }
867
868 /**
869 * Updates the source text for this item to match the current size of the
870 * image.
871 *
872 */
873 private void updateSource() {
874 StringBuffer newText = new StringBuffer(getTagText());
875
876 switch (_scaleType) {
877 case (RATIO):
878 DecimalFormat format = new DecimalFormat("0.00");
879 newText.append(format.format(_scale));
880 break;
881 case (WIDTH):
882 newText.append(getWidth());
883 break;
884 }
885
886 scaleCrop();
887
888 // If the image is cropped add the position for the start and finish of
889 // the crop to the soure text
890 if (_start.x > 0 || _start.y > 0 || _end.x != _image.getWidth()
891 || _end.y != _image.getHeight()) {
892 newText.append(" ").append(_start.x).append(" ").append(_start.y);
893 newText.append(" ").append(_end.x).append(" ").append(_end.y);
894 }
895
896 if(_flipX) {
897 newText.append(" flipX");
898 }
899 if(_flipY) {
900 newText.append(" flipY");
901 }
902 if(Double.compare(_rotate, 0) != 0) {
903 newText.append(" rotation=" + _rotate);
904 }
905
906 _source.setText(newText.toString());
907 }
908
909 @Override
910 public void translate(Point origin, double ratio) {
911 _scale *= ratio;
912 updateSource();
913 super.translate(origin, ratio);
914 }
915
916 @Override
917 public AxisAlignedBoxBounds getDrawingArea() {
918
919 AxisAlignedBoxBounds da = super.getDrawingArea();
920
921 if (getLink() != null || hasAction()) {
922 AxisAlignedBoxBounds linkBounds = AxisAlignedBoxBounds.getEnclosing(getLinkBounds());
923 linkBounds.getTopLeft().add(getX() - LEFT_MARGIN, getY() + getLinkYOffset());
924 linkBounds.getSize().width += 2;
925 linkBounds.getSize().height += 2;
926 da.combineWith(linkBounds);
927 }
928
929 return da;
930
931 }
932
933 @Override
934 public void scale(Float scale, int originX, int originY) {
935 setScale(getScale() * scale);
936 super.scale(scale, originX, originY);
937 }
938
939 public void setFlipX(boolean flip) {
940 _flipX = flip;
941 }
942
943 public void setFlipY(boolean flip) {
944 _flipY = flip;
945 }
946
947 public boolean getFlipX() {
948 return _flipX;
949 }
950
951 public boolean getFlipY() {
952 return _flipY;
953 }
954
955 public void setRotate(double rotate) {
956 _rotate = rotate;
957 updateSource();
958 invalidateBounds();
959 }
960
961 public double getRotate() {
962 return _rotate;
963 }
964
965 public boolean MouseOverBackgroundPixel(int mouseX, int mouseY, Colour bg_col)
966 {
967 int base_x = (_anchorLeft!=null) ? _anchorLeft : _source.getX();
968 int base_y = (_anchorTop!=null) ? _anchorTop : _source.getY();
969 int x = mouseX - base_x;
970 int y = mouseY - base_y;
971
972 Colour c = _image.getPixel(x, y);
973
974 int c_red = c.getRed255();
975 int c_green = c.getGreen255();
976 int c_blue = c.getBlue255();
977
978 int bg_red = (bg_col!=null) ? bg_col.getRed255() : 0xff;
979 int bg_green = (bg_col!=null) ? bg_col.getGreen255() : 0xff;
980 int bg_blue = (bg_col!=null) ? bg_col.getBlue255() : 0xff;
981
982 int red_diff = Math.abs(c_red - bg_red);
983 int green_diff = Math.abs(c_green - bg_green);
984 int blue_diff = Math.abs(c_blue - bg_blue);
985
986 return ((red_diff<=2) && (green_diff<=2) && (blue_diff<=2));
987
988 }
989}
Note: See TracBrowser for help on using the repository browser.