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

Last change on this file since 1143 was 1143, checked in by bln4, 6 years ago

Used Eclipse refactoring to encapsulate Point.X and Point.Y

File size: 26.1 KB
RevLine 
[919]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
[4]19package org.expeditee.items;
20
21import java.io.File;
[601]22import java.io.IOException;
23import java.text.DecimalFormat;
[4]24
[1047]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;
[1102]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;
[88]43import org.expeditee.gui.FrameGraphics;
[843]44import org.expeditee.gui.FrameIO;
45import org.expeditee.gui.FrameUtils;
[4]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 */
[601]64public class Picture extends XRayable {
[1102]65
66 private static final float CROPPING_COMPOSITE_ALPHA = 0.5f;
[4]67
[133]68 private static final int MINIMUM_WIDTH = 10;
[601]69
[4]70 public static final int WIDTH = 0;
[601]71
[4]72 public static final int RATIO = 1;
73
[78]74 protected Image _image = null;
[4]75
76 private int _scaleType = RATIO;
77
78 private float _scale = 1.0f;
79
[601]80 // Start of the crop relative to START
[4]81 private Point _cropStart = null;
[601]82
83 // Start of the crop relative to END
[4]84 private Point _cropEnd = null;
85
[376]86 private Point _start = new Point(0, 0);
[601]87
[376]88 private Point _end = new Point(0, 0);
[643]89
[653]90 private double _rotate = 0;
91
[643]92 private boolean _flipX = false;
93 private boolean _flipY = false;
[80]94
[4]95 private boolean _showCropping = false;
96
[1047]97 protected Integer _anchorLeft = null;
98 protected Integer _anchorTop = null;
99
[147]100 private String _path = "";
[226]101
[601]102 private String _size = "";
103
104 private String _fileName = null;
105
[1102]106 protected Picture(Text source, Image image) {
[601]107 super(source);
108 _image = image;
109
110 refresh();
111
[1047]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)
[601]117 parseSize();
[1047]118 }
[601]119 }
120
[4]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 *
[601]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
[4]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 */
[1102]138 public Picture(Text source, String fileName, String path, String size)
139 {
[601]140 super(source);
141 _fileName = fileName;
142 _path = path;
[1047]143
144 String size_without_options = parseMinusOptions(size);
145 _size = size_without_options;
[601]146
147 refresh();
148 parseSize();
[78]149 }
[601]150
151 protected String getImageSize() {
152 return _size;
[147]153 }
[124]154
[1047]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 }
[601]206 protected void parseSize() {
207 String size = getImageSize();
208
[1143]209 if (_end.getX() != 0 || _end.getY() != 0)
[601]210 return;
211
[80]212 // set the default values for start and end
[1102]213 _start.set(0, 0);
[108]214 if (_image == null)
[1102]215 _end.set(0, 0);
[108]216 else
[1102]217 _end.set(_image.getWidth(), _image.getHeight());
[80]218 size = size.trim();
[653]219 String sizeLower = size.toLowerCase();
[80]220 String[] values = size.split("\\s+");
221 // Now get the cropping values if there are any
[4]222 try {
[80]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 }
[609]232 scaleCrop();
[80]233 }
234 } catch (Exception e) {
235 }
[643]236
[653]237 if(sizeLower.contains("flipx")) {
[643]238 _flipX = true;
239 }
240
[653]241 if(sizeLower.contains("flipy")) {
[643]242 _flipY = true;
243 }
[653]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 }
[80]256
257 try {
[4]258 if (size.length() == 0) {
[1102]259 size = "" + _image.getWidth();
[601]260 _source.setText(getTagText() + size);
[80]261 return;
[4]262 }
[80]263 size = values[0];
264 // parse width or ratio from text
265 if (size.contains(".")) {
[4]266 // this is a ratio
[80]267 _scale = Float.parseFloat(size);
[4]268 _scaleType = RATIO;
269 } else if (size.length() > 0) {
270 // this is an absolute width
[80]271 int width = Integer.parseInt(size);
[4]272 _scaleType = WIDTH;
[80]273 setWidth(width);
[4]274 }
275 } catch (Exception e) {
[80]276 _scale = 1F;
[4]277 }
278 }
279
[1102]280 public void setStartCrop(Point p)
281 {
[1143]282 if (p != null) setStartCrop(p.getX(), p.getY());
[1102]283 }
284
[4]285 public void setStartCrop(int x, int y) {
[124]286 invalidateCroppedArea();
[4]287 _cropStart = new Point(x - getX(), y - getY());
[124]288 invalidateCroppedArea();
[4]289 }
290
[1102]291 public void setEndCrop(Point p)
292 {
[1143]293 if (p != null) setEndCrop(p.getX(), p.getY());
[1102]294 }
295
[4]296 public void setEndCrop(int x, int y) {
[124]297 invalidateCroppedArea();
[4]298 _cropEnd = new Point(x - getX(), y - getY());
[124]299 invalidateCroppedArea();
[4]300 }
301
[124]302 private void invalidateCroppedArea() {
303 if (_cropStart != null && _cropEnd != null) {
304 Point topLeft = getTopLeftCrop();
305 Point bottomRight = getBottomRightCrop();
[1143]306 int startX = getX() + topLeft.getX() - _highlightThickness;
307 int startY = getY() + topLeft.getY() - _highlightThickness;
[124]308 int border = 2 * _highlightThickness;
[1102]309 // TODO: Why invalidate specific area just before invalidateAll? cts16
310 invalidate(new AxisAlignedBoxBounds(startX, startY,
[1143]311 bottomRight.getX() - topLeft.getX() + 2 * border, bottomRight.getY() - topLeft.getY() + 2 * border));
[124]312 invalidateAll();
313 } else {
314 invalidateAll();
315 }
316 }
317
[80]318 public Point getTopLeftCrop() {
[1143]319 return new Point(Math.min(_cropStart.getX(), _cropEnd.getX()), Math.min(
320 _cropStart.getY(), _cropEnd.getY()));
[80]321 }
322
[1102]323 public Point getBottomRightCrop()
324 {
[1143]325 return new Point(Math.max(_cropStart.getX(), _cropEnd.getX()), Math.max(_cropStart.getY(), _cropEnd.getY()));
[80]326 }
327
[4]328 public void setShowCrop(boolean value) {
[130]329 // invalidateCroppedArea();
[4]330 _showCropping = value;
[124]331 invalidateCroppedArea();
[4]332 }
[1102]333
334 public boolean isBeingCropped()
335 {
336 return (_cropStart != null && _cropEnd != null);
337 }
[4]338
[1102]339 public boolean isCropTooSmall()
340 {
341 if (!isBeingCropped()) return true;
[4]342
[1143]343 int cropWidth = Math.abs(_cropEnd.getX() - _cropStart.getX());
344 int cropHeight = Math.abs(_cropEnd.getY() - _cropStart.getY());
[80]345
346 return cropWidth < MINIMUM_WIDTH || cropHeight < MINIMUM_WIDTH;
[4]347 }
348
349 public void clearCropping() {
[124]350 invalidateCroppedArea();
[4]351 _cropStart = null;
352 _cropEnd = null;
353 setShowCrop(false);
354 }
355
[1102]356 public PolygonBounds updateBounds()
357 {
[108]358 if (_image == null) {
359 refresh();
[601]360 parseSize();
[108]361 }
[653]362
363 Point[] ori = new Point[4];
364 Point centre = new Point();
[4]365
[1047]366 int base_x = (_anchorLeft!=null) ? _anchorLeft : _source.getX();
367 int base_y = (_anchorTop!=null) ? _anchorTop : _source.getY();
368
[4]369 if (_cropStart == null || _cropEnd == null) {
[80]370 int width = getWidth();
371 int height = getHeight();
[653]372
[1143]373 centre.setX(base_x + width / 2);
374 centre.setY(base_y + height / 2);
[4]375
[108]376 int xdiff = -MARGIN_RIGHT; // -getLeftMargin();
[4]377
378 // extra pixel around the image so the highlighting is visible
[653]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
[1047]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);
[653]388
[4]389 } else {
[80]390 Point topLeft = getTopLeftCrop();
391 Point bottomRight = getBottomRightCrop();
[653]392
[1143]393 centre.setX(base_x + (bottomRight.getX() - topLeft.getX()) / 2);
394 centre.setY(base_y + (bottomRight.getY() - topLeft.getY()) / 2);
[653]395
[1143]396 AxisAlignedBoxBounds clip = new AxisAlignedBoxBounds(topLeft.getX() + base_x,
397 topLeft.getY() + base_y, bottomRight.getX() - topLeft.getX(),
398 bottomRight.getY() - topLeft.getY());
[653]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);
[4]408
409 }
[653]410
[1102]411 PolygonBounds poly = new PolygonBounds();
412 for (Point p : ori) {
413 poly.addPoint(p);
414 }
415 poly.rotate(Math.PI * _rotate / 180, centre);
[653]416
[1102]417 return poly.close();
[4]418 }
419
420 @Override
[108]421 public double getEnclosedArea() {
422 return getWidth() * getHeight();
[4]423 }
424
425 @Override
[400]426 public void setWidth(Integer width) {
[1143]427 _scale = width * 1F / (_end.getX() - _start.getX());
[80]428 }
429
[376]430 public Point getStart() {
431 return _start;
432 }
433
434 public Point getEnd() {
435 return _end;
436 }
437
[80]438 /**
439 * Gets the width with which the picture is displayed on the screen.
440 */
441 @Override
[400]442 public Integer getWidth() {
[609]443 return Math.round(getUnscaledWidth() * _scale);
[80]444 }
445
446 /**
447 * Gets the height with which the picture is displayed on the screen.
448 */
[282]449 @Override
[80]450 public int getHeight() {
[609]451 return Math.round(getUnscaledHeight() * _scale);
[80]452 }
453
[88]454 /**
455 * Dont paint links in audience mode for images.
456 */
[80]457 @Override
[1102]458 protected void paintLink()
459 {
460 if (DisplayController.isAudienceMode()) return;
461 super.paintLink();
[88]462 }
[607]463
[1102]464 /**
465 * Paint the image repeatedly tiled over the drawing area.
466 */
467 public void paintImageTiling()
468 {
469 if (_image == null) return;
[607]470
[1102]471 int iw = _image.getWidth();
472 int ih = _image.getHeight();
473 if(iw <= 0 || ih <= 0) return;
[607]474
[1102]475 int base_x = (_anchorLeft != null) ? _anchorLeft : _source.getX();
476 int base_y = (_anchorTop != null) ? _anchorTop : _source.getY();
[609]477
[1047]478 int dX1 = base_x;
479 int dY1 = base_y;
480 int dX2 = base_x + getWidth();
481 int dY2 = base_y + getHeight();
482
[1102]483 Image tmp = Image.createImage(getWidth(), getHeight());
484 EcosystemManager.getGraphicsManager().pushDrawingSurface(tmp);
485
[653]486 int offX = (tmp.getWidth() - getWidth()) / 2;
487 int offY = (tmp.getHeight() - getHeight()) / 2;
[610]488
[1143]489 int cropStartX = _start.getX();
490 int cropEndX = _end.getX();
[610]491 if(cropEndX > iw) {
492 cropEndX = iw;
493 }
[1102]494
[610]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
[1143]505 int cropStartY = _start.getY();
506 int cropEndY = _end.getY();
[610]507 if(cropEndY > ih) {
508 cropEndY = ih;
509 }
[1102]510
[610]511 for(int y = dY1; y < dY2; ) {
512 int h = (int) ((cropEndY - cropStartY) * _scale);
513 int endY = y + h;
[607]514 if(endY > dY2) {
515 endY = dY2;
[610]516 cropEndY = cropStartY + (int) ((dY2 - y) / _scale);
[607]517 }
[610]518
[643]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
[1102]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
[610]532 cropStartY = 0;
533 cropEndY = ih;
534
535 y = endY;
[607]536 }
[610]537
538 cropStartX = 0;
539 cropEndX = iw;
540
541 x = endX;
[607]542 }
[1102]543
544 EcosystemManager.getGraphicsManager().popDrawingSurface();
545 EcosystemManager.getGraphicsManager().drawImage(tmp, new Point(dX1, dY1), null, Math.PI * _rotate / 180);
546 tmp.releaseImage();
[607]547 }
[108]548
[88]549 @Override
[1102]550 public void paint()
551 {
552 if (_image == null) return;
[4]553
[1102]554 paintLink();
555
556 GraphicsManager g = EcosystemManager.getGraphicsManager();
[4]557
[1102]558 // if we are showing the cropping
[80]559 if (_showCropping && !isCropTooSmall()) {
[1102]560 // show the uncropped area as transparent
561 g.setCompositeAlpha(CROPPING_COMPOSITE_ALPHA);
562 paintImageTiling();
563 g.setCompositeAlpha(1.0f);
[607]564
[4]565 // show the cropped area normally
[80]566 Point topLeft = getTopLeftCrop();
567 Point bottomRight = getBottomRightCrop();
[1102]568 int base_x = (_anchorLeft != null) ? _anchorLeft : _source.getX();
569 int base_y = (_anchorTop != null) ? _anchorTop : _source.getY();
[1047]570
[1143]571 Clip clip = new Clip(new AxisAlignedBoxBounds( base_x + topLeft.getX(),
572 base_y + topLeft.getY(),
573 bottomRight.getX() - topLeft.getX(),
574 bottomRight.getY() - topLeft.getY()));
[1102]575 EnforcedClipKey key = g.pushClip(clip);
576 paintImageTiling();
577 g.popClip(key);
[4]578
[1102]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
[4]583 } else {
[1102]584 paintImageTiling();
[4]585 }
[80]586
[1102]587 PolygonBounds poly = (PolygonBounds) getBounds();
588
[242]589 if (hasVisibleBorder()) {
[1102]590 Stroke borderStroke = new Stroke(getThickness(), DEFAULT_CAP, DEFAULT_JOIN);
591 g.drawPolygon(poly, null, null, 0.0, null, getPaintBorderColor(), borderStroke);
[80]592 }
[376]593
[242]594 if (isHighlighted()) {
[1102]595 Stroke borderStroke = new Stroke(1, DEFAULT_CAP, DEFAULT_JOIN);
596 g.drawPolygon(poly, null, null, 0.0, null, getHighlightColor(), borderStroke);
[242]597 }
[4]598 }
[376]599
[242]600 @Override
[1102]601 public Colour getHighlightColor()
602 {
603 if (_highlightColour.equals(getBorderColor())) return ALTERNATE_HIGHLIGHT;
604 return _highlightColour;
[242]605 }
[4]606
[1102]607 protected Picture createPicture()
608 {
609 return ItemUtils.CreatePicture((Text) _source.copy());
[4]610 }
611
612 @Override
613 public Picture copy() {
[78]614 Picture p = createPicture();
[80]615 p._image = _image;
[1102]616 p._highlightMode = _highlightMode;
[108]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
[4]620
[97]621 if (!isCropTooSmall() && _cropStart != null && _cropEnd != null) {
[80]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();
[1143]627 int startX = Math.round(topLeft.getX() / _scale) + _start.getX();
628 int startY = Math.round(topLeft.getY() / _scale) + _start.getY();
629 int endX = Math.round(bottomRight.getX() / _scale + _start.getX());
630 int endY = Math.round(bottomRight.getY() / _scale + _start.getY());
[1102]631 int width = _image.getWidth();
632 int height = _image.getHeight();
[80]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);
[1047]649 int base_x = (_anchorLeft!=null) ? _anchorLeft : _source.getX();
650 int base_y = (_anchorTop!=null) ? _anchorTop : _source.getY();
[1143]651 p._source.setPosition(topLeft.getX() + base_x, topLeft.getY() + base_y);
[80]652 } else {
[609]653 p._start = new Point(_start);
654 p._end = new Point(_end);
[80]655 }
656 p._scale = _scale;
657 p._scaleType = _scaleType;
658 p._path = _path;
[601]659 p._fileName = _fileName;
[4]660
[601]661 p.updateSource();
[1102]662 p.invalidateBounds();
[4]663
664 return p;
665 }
666
[108]667 public float getScale() {
668 return _scale;
[4]669 }
[124]670
671 public void setScale(float scale) {
[108]672 _scale = scale;
[4]673 }
[603]674
[609]675 public void scaleCrop() {
676 // scale crop values to within image bounds
[1102]677 int iw = _image.getWidth();
678 int ih = _image.getHeight();
[609]679 if(iw > 0 || ih > 0) {
[1143]680 while(_start.getX() >= iw) {
681 _start.setX(_start.getX() - iw);
682 _end.setX(_end.getX() - iw);
[609]683 }
[1143]684 while(_start.getY() >= ih) {
685 _start.setY(_start.getY() - ih);
686 _end.setY(_end.getY() - ih);
[609]687 }
[1143]688 while(_start.getX() < 0) {
689 _start.setX(_start.getX() + iw);
690 _end.setX(_end.getX() + iw);
[609]691 }
[1143]692 while(_start.getY() < 0) {
693 _start.setY(_start.getY() + ih);
694 _end.setY(_end.getY() + ih);
[609]695 }
696 }
697 }
698
[603]699 public void setCrop(int startX, int startY, int endX, int endY) {
700 _start = new Point(startX, startY);
701 _end = new Point(endX, endY);
[610]702 updateSource();
[603]703 }
[124]704
[4]705 @Override
[108]706 public float getSize() {
[601]707 return _source.getSize();
[4]708 }
709
710 @Override
[108]711 public void setSize(float size) {
[601]712 float diff = size - _source.getSize();
[4]713 float oldScale = _scale;
714
[108]715 float multiplier = (1000F + diff * 40F) / 1000F;
716 _scale = _scale * multiplier;
[86]717
[80]718 // picture must still be at least XX pixels wide
[108]719 if (getWidth() < MINIMUM_WIDTH) {
[4]720 _scale = oldScale;
[108]721 } else {
[1102]722 _source.translate(EcosystemManager.getInputManager().getCursorPosition(), multiplier);
[4]723 }
[601]724 updateSource();
[1102]725 invalidateBounds();
[147]726 // Make sure items that are resized display the border
[130]727 invalidateAll();
[4]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
[226]744 public Image getCroppedImage() {
[376]745 if (_image == null)
[282]746 return null;
[376]747 if (!isCropped()) {
748 return _image;
749 }
750
[1143]751 return Image.createImageAsCroppedCopy(_image, _start.getX(), _start.getY(), getUnscaledWidth(), getUnscaledHeight());
[226]752 }
753
[376]754 public int getUnscaledWidth() {
[1143]755 return _end.getX() - _start.getX();
[376]756 }
757
758 public int getUnscaledHeight() {
[1143]759 return _end.getY() - _start.getY();
[376]760 }
761
762 /**
763 * @return true if this is a cropped image.
764 */
765 public boolean isCropped() {
[1143]766 return (_end.getX() != 0 && _end.getX() != _image.getWidth()) || (_end.getY() != 0 && _end.getY() != _image.getHeight()) || _start.getY() != 0 || _start.getX() != 0;
[376]767 }
768
[601]769 @Override
[78]770 public boolean refresh() {
771 // ImageIcon is faster, but cannot handle some formats
[147]772 // (notably.bmp) hence, we try this first, then if it fails we try
[78]773 // ImageIO
[1102]774 /*
[147]775 try {
776 _image = new ImageIcon(_path).getImage();
777 } catch (Exception e) {
778 }
[78]779
780 // if ImageIcon failed to read the image
[1102]781 if (_image == null || _image.getWidth() <= 0) {
[78]782 try {
783 _image = ImageIO.read(new File(_path));
[601]784 } catch (IOException e) {
[78]785 // e.printStackTrace();
[601]786 Logger.Log(e);
787 _image = null;
[80]788 return false;
[7]789 }
790 }
[1102]791*/
792 _image = Image.getImage(_path);
[78]793 return true;
[7]794 }
795
[78]796 @Override
797 protected int getLinkYOffset() {
798 return getBoundsHeight() / 2;
799 }
800
801 @Override
802 public void setLinkMark(boolean state) {
[147]803 // TODO use the more efficient invalidiate method
804 // The commented code below is not quite working
[130]805 // if(!state)
806 // invalidateCommonTrait(ItemAppearence.LinkChanged);
[601]807 _source.setLinkMark(state);
[130]808 // if(state)
809 // invalidateCommonTrait(ItemAppearence.LinkChanged);
810 invalidateAll();
[78]811 }
812
813 @Override
814 public void setActionMark(boolean state) {
[130]815 // if (!state)
816 // invalidateCommonTrait(ItemAppearence.LinkChanged);
[601]817 _source.setActionMark(state);
[130]818 // if (state)
819 // invalidateCommonTrait(ItemAppearence.LinkChanged);
820 invalidateAll();
[78]821 }
822
823 @Override
[601]824 public boolean getLinkMark() {
[1102]825 return !DisplayController.isAudienceMode() && _source.getLinkMark();
[601]826 }
827
828 @Override
829 public boolean getActionMark() {
830 return _source.getActionMark();
831 }
832
833 @Override
[80]834 public String getName() {
[601]835 return _fileName;
[80]836 }
[843]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 }
[80]863
[601]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 }
[609]885
886 scaleCrop();
[601]887
888 // If the image is cropped add the position for the start and finish of
889 // the crop to the soure text
[1143]890 if (_start.getX() > 0 || _start.getY() > 0 || _end.getX() != _image.getWidth()
891 || _end.getY() != _image.getHeight()) {
892 newText.append(" ").append(_start.getX()).append(" ").append(_start.getY());
893 newText.append(" ").append(_end.getX()).append(" ").append(_end.getY());
[601]894 }
[643]895
896 if(_flipX) {
897 newText.append(" flipX");
898 }
899 if(_flipY) {
900 newText.append(" flipY");
901 }
[653]902 if(Double.compare(_rotate, 0) != 0) {
903 newText.append(" rotation=" + _rotate);
904 }
[601]905
906 _source.setText(newText.toString());
907 }
908
[108]909 @Override
[1102]910 public void translate(Point origin, double ratio) {
[108]911 _scale *= ratio;
[601]912 updateSource();
[108]913 super.translate(origin, ratio);
914 }
[121]915
916 @Override
[1102]917 public AxisAlignedBoxBounds getDrawingArea() {
[124]918
[1102]919 AxisAlignedBoxBounds da = super.getDrawingArea();
[124]920
[133]921 if (getLink() != null || hasAction()) {
[1102]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);
[124]927 }
928
[121]929 return da;
[124]930
[121]931 }
[376]932
[311]933 @Override
[376]934 public void scale(Float scale, int originX, int originY) {
[311]935 setScale(getScale() * scale);
936 super.scale(scale, originX, originY);
937 }
[643]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 }
[653]954
955 public void setRotate(double rotate) {
956 _rotate = rotate;
957 updateSource();
[1102]958 invalidateBounds();
[653]959 }
960
961 public double getRotate() {
962 return _rotate;
963 }
[124]964
[1102]965 public boolean MouseOverBackgroundPixel(int mouseX, int mouseY, Colour bg_col)
[1047]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
[1102]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();
[1047]977
[1102]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;
[1047]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 }
[4]989}
Note: See TracBrowser for help on using the repository browser.