source: trunk/org/expeditee/items/Picture.java@ 4

Last change on this file since 4 was 4, checked in by davidb, 16 years ago

Starting source code to Expeditee

File size: 12.7 KB
Line 
1package org.expeditee.items;
2
3import java.awt.AlphaComposite;
4import java.awt.Color;
5import java.awt.Dimension;
6import java.awt.Graphics2D;
7import java.awt.Image;
8import java.awt.Point;
9import java.awt.Polygon;
10import java.awt.Rectangle;
11import java.awt.Shape;
12import java.awt.geom.AffineTransform;
13import java.awt.image.ImageObserver;
14import java.io.File;
15import java.io.IOException;
16import java.text.DecimalFormat;
17import java.util.List;
18
19import javax.imageio.ImageIO;
20import javax.swing.ImageIcon;
21
22import org.expeditee.gui.Frame;
23import org.expeditee.gui.FrameGraphics;
24import org.expeditee.gui.FrameIO;
25import org.expeditee.io.Logger;
26
27/**
28 * This class represents an Image loaded from a file which is shown on the
29 * screen. Loading of the Image from disk occurs in the constructor, and takes
30 * approximately one second per mb of the Image file size. <br>
31 * <br>
32 * Currently Supported (Tested) Image formats:<br>
33 * BMP<br>
34 * JPG<br>
35 * GIF<br>
36 * GIF (Animated)<br>
37 * <br>
38 * Currently only the default size of the Image is supported, but future
39 * versions may support scaling.
40 *
41 * @author jdm18
42 *
43 */
44public class Picture extends Item {
45
46 public static final int WIDTH = 0;
47
48 public static final int RATIO = 1;
49
50 public static final int FRACTION = 2;
51
52 private Image _image = null;
53
54 private Polygon _poly = null;
55
56 // private Polygon _circle = null;
57 private Text _source = null;
58
59 // true if this is a frame picture
60 private boolean _isFrame = false;
61
62 private int _numerator = -1;
63
64 private int _denominator = -1;
65
66 private float _ratio = 1.0f;
67
68 private int _width = -1;
69
70 private int _scaleType = RATIO;
71
72 private float _scale = 1.0f;
73
74 private Point _cropStart = null;
75
76 private Point _cropEnd = null;
77
78 private Point _cropOrigin = null;
79
80 private boolean _showCropping = false;
81
82 // used to repaint animated GIF images, among other things.
83 private ImageObserver _imageObserver = null;
84
85 /**
86 * Creates a new Picture from the given path. The ImageObserver is optional
87 * and can be set to NULL. <br>
88 * Note: It is assumed that the file described in path has already been
89 * checked to exist.
90 *
91 * @param source
92 * The Text Item that was used to create this Picture
93 * @param path
94 * The Path of the Image to load from disk.
95 * @param observer
96 * The ImageObserver to assign when painting the Image on the
97 * screen.
98 */
99 public Picture(Text source, String path, String size, ImageObserver observer)
100 throws IllegalArgumentException {
101 super();
102 _source = source;
103 _imageObserver = observer;
104
105 // Check if the picture is being created with @f
106 if (path == null && source.getLink() != null) {
107 String link = source.getLink();
108 if (FrameIO.isPositiveInteger(link)){
109 link = source.getParent().getFramesetNameAdjusted() + link;
110 }
111 Frame frame = FrameIO.LoadFrame(link);
112 FrameGraphics.UpdateBuffer(frame, false);
113 _image = frame.getBuffer();
114 _isFrame = true;
115
116 } else {
117
118 // ImageIcon is faster, but cannot handle some formats
119 // (notably
120 // .bmp)
121 // hence, we try this first, then if it fails we try
122 // ImageIO
123 _image = new ImageIcon(path).getImage();
124
125 // if ImageIcon failed to read the image
126 if (_image.getWidth(null) <= 0) {
127 try {
128 _image = ImageIO.read(new File(path));
129 } catch (IOException e) {
130 // e.printStackTrace();
131 Logger.Log(e);
132 _image = null;
133 }
134 }
135 }
136
137 if (_image == null)
138 throw new IllegalArgumentException();
139
140 try {
141 if (size.length() == 0) {
142 size = "" + _image.getWidth(null);
143 _source.setText(_source.getFirstLine() + " " + size);
144 }
145
146 // parse size from text
147 if (size.contains("/")) {
148 // this is a fraction
149 _numerator = Integer.parseInt(size.substring(0, size
150 .indexOf("/")));
151 _denominator = Integer.parseInt(size.substring(size
152 .indexOf("/") + 1));
153 _scale = (_numerator * 1.0f) / _denominator;
154 _scaleType = FRACTION;
155 } else if (size.contains(".")) {
156 // this is a ratio
157 _ratio = Float.parseFloat(size);
158 _scaleType = RATIO;
159 _scale = _ratio;
160 } else if (size.length() > 0) {
161 // this is an absolute width
162 _width = Integer.parseInt(size);
163 _scaleType = WIDTH;
164 _scale = _width / (_image.getWidth(null) * 1.0f);
165 }
166 } catch (Exception e) {
167 size = "" + _image.getWidth(null);
168 _source.setText("@f " + size);
169 FrameGraphics.ErrorMessage("Invalid argument for @f tag");
170 }
171
172 }
173
174 public void setStartCrop(int x, int y) {
175 _cropStart = new Point(x - getX(), y - getY());
176 _cropOrigin = new Point(getX(), getY());
177 }
178
179 public void setEndCrop(int x, int y) {
180 _cropEnd = new Point(x - getX(), y - getY());
181 }
182
183 public void setShowCrop(boolean value) {
184 _showCropping = value;
185 }
186
187 public int getCroppedSize() {
188 if (_cropStart == null || _cropEnd == null)
189 return 0;
190
191 int diff = (_cropEnd.x - _cropStart.x);
192 diff *= (_cropEnd.y - _cropStart.y);
193 return diff;
194 }
195
196 public void clearCropping() {
197 _cropStart = null;
198 _cropEnd = null;
199 setShowCrop(false);
200 }
201
202 protected void updatePolygon() {
203 if (_image == null)
204 return;
205
206 _poly = new Polygon();
207
208 if (_cropStart == null || _cropEnd == null) {
209 int width = _image.getWidth(null);
210 int height = _image.getHeight(null);
211
212 width *= _scale;
213 height *= _scale;
214
215 int xdiff = 0;
216
217 if (getLink() != null || getAction() != null) {
218 xdiff -= 10;
219 }
220
221 // extra pixel around the image so the highlighting is visible
222 _poly.addPoint(_source.getX() - 1 + xdiff, _source.getY() - 1);
223 _poly.addPoint(_source.getX() + 1 + width, _source.getY() - 1);
224 _poly.addPoint(_source.getX() + 1 + width, _source.getY() + 1
225 + height);
226 _poly.addPoint(_source.getX() - 1 + xdiff, _source.getY() + 1
227 + height);
228 } else {
229 Rectangle clip = new Rectangle(_source.getX() + _cropStart.x
230 + _cropOrigin.x, _source.getY() + _cropStart.y
231 + _cropOrigin.y, _cropEnd.x - _cropStart.x, _cropEnd.y
232 - _cropStart.y).getBounds();
233 _poly.addPoint((int) clip.getMinX() - 1, (int) clip.getMinY() - 1);
234 _poly.addPoint((int) clip.getMinX() - 1, (int) clip.getMaxY() + 1);
235 _poly.addPoint((int) clip.getMaxX() + 1, (int) clip.getMaxY() + 1);
236 _poly.addPoint((int) clip.getMaxX() + 1, (int) clip.getMinY() - 1);
237
238 }
239 }
240
241 /**
242 * Returns the Text Item that was used to create this Picture object. This
243 * is required by KMSWriter for saving the Frame.
244 *
245 * @return The Text Item used to create this Picture.
246 */
247 public Text getText() {
248 return _source;
249 }
250
251 @Override
252 public int getID() {
253 return _source.getID();
254 }
255
256 @Override
257 public void setID(int newID) {
258 _source.setID(newID);
259 }
260
261 @Override
262 public int getY() {
263 return _source.getY();
264 }
265
266 @Override
267 public int getX() {
268 return _source.getX();
269 }
270
271 @Override
272 public void setPosition(int x, int y) {
273 _source.setPosition(x, y);
274
275 updatePolygon();
276 }
277
278 @Override
279 public String getLink() {
280 if (_source == null)
281 return null;
282 else
283 return _source.getLink();
284 }
285
286 @Override
287 public void setLink(String frameName) {
288 if (_source == null)
289 return;
290 else {
291 _source.setLink(frameName);
292
293 if (_isFrame) {
294 Frame frame = FrameIO.LoadFrame(_source.getLink());
295 // if (frame.getBuffer() == null)
296 FrameGraphics.UpdateBuffer(frame, false);
297 _image = frame.getBuffer();
298 }
299 }
300
301 updatePolygon();
302 }
303
304 @Override
305 public void setAction(List<String> action) {
306 if (_source == null)
307 return;
308 else
309 _source.setAction(action);
310
311 updatePolygon();
312 }
313
314 @Override
315 public List<String> getAction() {
316 if (_source == null)
317 return null;
318 else
319 return _source.getAction();
320 }
321
322 @Override
323 public void paint(Graphics2D g) {
324 if (_image == null)
325 return;
326
327 int width = _image.getWidth(null);
328 int height = _image.getHeight(null);
329
330 width *= _scale;
331 height *= _scale;
332
333 if (getLink() != null || getAction() != null) {
334 g.setColor(getPaintHighlightColor());
335
336 AffineTransform at = new AffineTransform();
337 AffineTransform orig = g.getTransform();
338 at.translate(getX() - 12, getY() + (getBoundsHeight() / 2));
339 g.setTransform(at);
340
341 if (getLinkMark() && getLink() != null) {
342 g.drawPolygon(getCircle());
343
344 // if the link is not valid, cross out the circle
345 if (!isLinkValid())
346 g.drawPolygon(getCircleCross());
347 }
348
349 if (getActionMark() && getAction() != null) {
350 g.drawPolygon(getCircle());
351 g.fillPolygon(getCircle());
352
353 // if the link is not valid, cross out the circle
354 if (getLink() != null && !isLinkValid()) {
355 g.setColor(getPaintBackgroundColor());
356 g.drawPolygon(getCircleCross());
357 }
358 }
359 g.setTransform(orig);
360 }
361
362 if (isHighlighted()) {
363 g.setColor(getHighlightColor());
364 g.drawPolygon(getPolygon());
365 }
366
367 // if we are showing the cropping, then show the original as transparent
368 if (_showCropping && _cropStart != null && _cropEnd != null) {
369 // show the full image as transparent
370 float alpha = .5f;
371 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
372 alpha));
373 g.drawImage(_image, _source.getX(), _source.getY(), width, height,
374 _imageObserver);
375
376 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
377 1.0f));
378 // show the cropped area normally
379 Shape clip = new Rectangle(_source.getX() + _cropStart.x, _source
380 .getY()
381 + _cropStart.y, _cropEnd.x - _cropStart.x, _cropEnd.y
382 - _cropStart.y);
383 g.setColor(getPaintHighlightColor());
384 g.draw(clip);
385 g.setClip(clip);
386
387 g.drawImage(_image, _source.getX(), _source.getY(), width, height,
388 _imageObserver);
389 g.draw(clip);
390 // if the image is cropped, but we are not showing the cropping
391 } else if (_cropStart != null && _cropEnd != null) {
392 Shape clip = new Rectangle(_source.getX() + _cropStart.x
393 + _cropOrigin.x, _source.getY() + _cropStart.y
394 + _cropOrigin.y, _cropEnd.x - _cropStart.x, _cropEnd.y
395 - _cropStart.y);
396 g.setClip(clip);
397
398 g.drawImage(_image, _source.getX(), _source.getY(), width, height,
399 _imageObserver);
400
401 // otherwise, paint normally
402 } else {
403 g.drawImage(_image, _source.getX(), _source.getY(), width, height,
404 _imageObserver);
405 }
406 }
407
408 @Override
409 public Polygon getPolygon() {
410 if (_poly == null)
411 updatePolygon();
412
413 Polygon external = new Polygon(_poly.xpoints, _poly.ypoints,
414 _poly.npoints);
415 return external;
416 }
417
418 @Override
419 public int showHighlight(boolean val) {
420 super.showHighlight(val);
421
422 return Item.DEFAULT_CURSOR;
423 }
424
425 @Override
426 public Picture copy() {
427 Picture p;
428
429 if (_isFrame){
430 p = ItemUtils.CreateFramePicture((Text) _source.copy(),
431 _imageObserver);
432 }else
433 p = ItemUtils.CreatePicture((Text) _source.copy(), _imageObserver);
434
435 Item.DuplicateItem(this, p);
436
437 if (_cropStart != null)
438 p.setStartCrop(_cropStart.x, _cropStart.y);
439
440 if (_cropEnd != null)
441 p.setEndCrop(_cropEnd.x, _cropEnd.y);
442
443 return p;
444 }
445
446 @Override
447 public void setMaxSize(Dimension d) {
448 // TODO Auto-generated method stub
449 }
450
451 @Override
452 public Color getColor() {
453 return _source.getColor();
454 }
455
456 @Override
457 public void setColor(Color c) {
458 _source.setColor(c);
459 }
460
461 @Override
462 public int getSize() {
463 return _source.getSize();
464 }
465
466 @Override
467 public void setSize(int size) {
468 int diff = size - _source.getSize();
469 float oldScale = _scale;
470
471 int oldDenom = _denominator;
472 int oldNumer = _numerator;
473 float oldRatio = _ratio;
474 int oldWidth = _width;
475
476 switch (_scaleType) {
477 case (FRACTION):
478 _numerator += diff;
479 _scale = (_numerator * 1.0f) / _denominator;
480 break;
481 case (RATIO):
482 _ratio += (diff) * 0.05;
483 _scale = _ratio;
484 break;
485 case (WIDTH):
486 _width += 10 * diff;
487 _scale = _width / (_image.getWidth(null) * 1.0f);
488 break;
489 }
490
491 // picture must still be at least 10 pixels wide
492 if ((_scale * _image.getWidth(null)) <= 10) {
493 _scale = oldScale;
494
495 _denominator = oldDenom;
496 _numerator = oldNumer;
497 _ratio = oldRatio;
498 _width = oldWidth;
499 }
500
501 // update source text item
502 String line = _source.getFirstLine();
503
504 switch (_scaleType) {
505 case (FRACTION):
506 line = line.replace(oldNumer + "/" + oldDenom, _numerator + "/"
507 + _denominator);
508 break;
509 case (RATIO):
510 DecimalFormat format = new DecimalFormat("0.00");
511 line = line.replace(format.format(oldRatio), format.format(_ratio));
512 break;
513 case (WIDTH):
514 line = line.replace("" + oldWidth, "" + _width);
515 break;
516 }
517
518 _source.setText(line);
519 updatePolygon();
520 }
521
522 @Override
523 public void setAnnotation(boolean val) {
524 // TODO Auto-generated method stub
525
526 }
527
528 @Override
529 public boolean isAnnotation() {
530 // TODO Auto-generated method stub
531 return false;
532 }
533
534 /**
535 * Returns the Image that this Picture object is painting on the screen.
536 * This is used by Frame to repaint animated GIFs.
537 *
538 * @return The Image that this Picture object represents.
539 */
540 public Image getImage() {
541 return _image;
542 }
543
544 @Override
545 public Item merge(Item merger, int mouseX, int mouseY) {
546 return merger;
547 }
548
549}
Note: See TracBrowser for help on using the repository browser.