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

Last change on this file since 1431 was 1431, checked in by bln4, 5 years ago

Recoding of the Labels class to improve surrogate mode functionality. Surrogate mode is now maintained when you navigate from one frame to another, even if that frame has a different set of labels. Completely different sets of labels cause Expeditee to exit surrogate mode with a message to the user.

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