1 package de.desy.acop.video.displayer;
2
3
4
5
6 import java.awt.AlphaComposite;
7 import java.awt.BorderLayout;
8 import java.awt.Color;
9 import java.awt.Dimension;
10 import java.awt.Font;
11 import java.awt.FontMetrics;
12 import java.awt.Graphics;
13 import java.awt.Graphics2D;
14 import java.awt.Point;
15 import java.awt.Rectangle;
16 import java.awt.event.ActionEvent;
17 import java.awt.event.ActionListener;
18 import java.awt.event.MouseEvent;
19 import java.awt.event.MouseListener;
20 import java.awt.event.MouseMotionListener;
21 import java.awt.image.BufferedImage;
22 import java.beans.ExceptionListener;
23 import java.beans.PropertyChangeEvent;
24 import java.beans.PropertyChangeListener;
25 import java.io.File;
26 import java.net.MalformedURLException;
27 import java.net.URL;
28 import java.text.SimpleDateFormat;
29 import java.util.Calendar;
30 import java.util.Date;
31
32 import javax.swing.JPanel;
33 import javax.swing.SwingUtilities;
34 import javax.swing.Timer;
35
36 import de.desy.acop.video.timageio.TBufferedImage;
37 import de.desy.acop.video.timageio.TImageIO;
38 import de.desy.acop.video.timageio.TImageMetadata;
39 import de.desy.tine.types.IMAGE;
40 import de.desy.tine.types.IMAGE.FrameHeader;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class ImageDisplayer extends JPanel implements TineImageReceiver {
66
67 private static final long serialVersionUID = 310376L;
68
69 public static final String INVALID_CAMERA_PORT_NAME = "invalid";
70
71 public static final String PROPERTY_KEEP_ASPECT_RATIO = "keepAspectRatio";
72 public static final String PROPERTY_AOI_ZOOM = "AOIZoom";
73 public static final String PROPERTY_OVERLAY_STATE = "overlayState";
74 public static final String PROPERTY_IMAGE_ZOOM = "imageZoom";
75 public static final String PROPERTY_HISTOGRAM_EQUALISATION = "histogramEqualisation";
76 public static final String PROPERTY_HISTOGRAM_MIN = "histogramMin";
77 public static final String PROPERTY_HISTOGRAM_MAX = "histogramMax";
78 public static final String PROPERTY_COLOR_MAP = "colorMap";
79
80 private static final int TIMER_DELAY = 250;
81
82
83
84
85 private ImageCLUT _clut;
86
87
88
89
90
91 private ImageZoom _imageZoom = ImageZoom.AUTO;
92
93
94
95
96 private OverlayState _overlayState = OverlayState.AUTO;
97
98
99
100
101
102 private boolean _aoiZoom;
103
104
105
106
107
108
109
110 private boolean _keepAspectRatio;
111
112
113
114
115
116
117
118
119
120
121 private boolean _histogramEqualisation;
122
123
124
125
126 private int _histogramMin = UNDEFINED_CONDITION;
127
128
129
130
131 private int _histogramMax = UNDEFINED_CONDITION;
132
133
134
135
136
137
138 private boolean _isLiveTransfer;
139
140
141 private Object _lockHeaderUpdate = new Object();
142
143
144 private boolean _isMouseInCanvas;
145
146
147 private boolean _isMousePressed;
148
149
150
151
152
153 private IMAGE _errorScreen;
154
155
156
157
158
159
160 private IMAGE _timage;
161
162
163
164
165 private ImageCounter _imageCounter = new ImageCounter();
166
167
168
169
170 private BufferedImage _image;
171
172
173 private long _lastCameraPortId = UNDEFINED_CONDITION;
174
175
176
177
178 private Timer _timer;
179
180
181
182
183
184
185 private ExceptionListener _exceptionListener;
186
187
188 public ImageDisplayer() {
189 initialize();
190 }
191
192 @Override
193 protected void paintComponent(Graphics g) {
194
195
196 super.paintComponents(g);
197
198 if (_timer != null && _timer.isRunning())
199 _timer.stop();
200
201
202 BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
203 Graphics2D g2 = bi.createGraphics();
204 try {
205 drawFrame(g2, _image);
206 } finally {
207 g2.dispose();
208 }
209 g.drawImage(bi, 0, 0, null);
210
211
212 }
213
214
215
216
217
218
219 public IMAGE getIMAGE() {
220 return _timage;
221 }
222
223
224
225
226
227
228 public ImageCLUT getCLUT() {
229 return _clut;
230 }
231
232
233
234
235
236
237
238 public void setCLUT(ImageCLUT newValue) {
239 ImageCLUT oldValue = _clut;
240 _clut = newValue;
241 firePropertyChange(PROPERTY_COLOR_MAP, oldValue, newValue);
242 }
243
244
245
246
247
248
249
250 public void setColorMap(ColorMap newValue) {
251 System.out.println("ImageDisplayer.setColorMap()");
252 setCLUT(new ImageCLUT(newValue, _clut.getComponentSize()));
253 }
254
255
256
257
258
259
260 public ColorMap getColorMap() {
261 if (_clut == null)
262 throw null;
263 return _clut.getColorMap();
264 }
265
266
267
268
269
270
271
272 public void setImageZoom(ImageZoom newValue) {
273 ImageZoom oldValue = _imageZoom;
274 _imageZoom = newValue;
275 firePropertyChange(PROPERTY_IMAGE_ZOOM, oldValue, newValue);
276 }
277
278
279
280
281 public ImageZoom getImageZoom() {
282 return _imageZoom;
283 }
284
285
286
287
288
289
290
291 public void setOverlayState(OverlayState newValue) {
292 OverlayState oldValue = _overlayState;
293 _overlayState = newValue;
294 firePropertyChange(PROPERTY_OVERLAY_STATE, oldValue, newValue);
295 }
296
297
298
299
300 public OverlayState getOverlayState() {
301 return _overlayState;
302 }
303
304
305
306
307
308
309
310
311
312
313
314 public void setHistogramEqualisation(boolean newValue) {
315 boolean oldValue = _histogramEqualisation;
316 _histogramEqualisation = newValue;
317 firePropertyChange(PROPERTY_HISTOGRAM_EQUALISATION, oldValue, newValue);
318 }
319
320
321
322
323
324
325
326
327 public boolean isHistogramEqualisation() {
328 return _histogramEqualisation;
329 }
330
331
332
333
334
335
336
337 public void setHistogramMin(int newValue) {
338 int max = getHistogramMax();
339 validateMinMax(newValue, _clut.getMinValue(), (max == UNDEFINED_CONDITION ? _clut.getMaxValue() : max) - 1);
340 int oldValue = _histogramMin;
341 _histogramMin = newValue;
342 firePropertyChange(PROPERTY_HISTOGRAM_MIN, oldValue, newValue);
343 }
344
345
346
347
348
349
350 public int getHistogramMin() {
351 return _histogramMin;
352 }
353
354
355
356
357
358
359
360 public void setHistogramMax(int newValue) {
361 int min = getHistogramMin();
362 validateMinMax(newValue, (min == UNDEFINED_CONDITION ? _clut.getMinValue() : min) + 1, _clut.getMaxValue());
363 int oldValue = _histogramMax;
364 _histogramMax = newValue;
365 firePropertyChange(PROPERTY_HISTOGRAM_MAX, oldValue, newValue);
366 }
367
368 private void validateMinMax(int value, int min, int max) {
369 if (value < min || value > max)
370 throw new IllegalArgumentException(value + " is out of range ["
371 + min + ", " + max + "]");
372 }
373
374
375
376
377
378
379 public int getHistogramMax() {
380 return _histogramMax;
381 }
382
383
384
385
386
387
388
389
390
391 public void setAOIZoom(boolean aoiZoom) {
392 boolean oldValue = _aoiZoom;
393 _aoiZoom = aoiZoom;
394 firePropertyChange(PROPERTY_AOI_ZOOM, oldValue, aoiZoom);
395 }
396
397
398
399
400
401
402
403
404 public boolean isAOIZoom() {
405 return _aoiZoom;
406 }
407
408
409
410
411
412
413
414
415
416 public void setKeepAspectRatio(boolean keepAspectRatio) {
417 boolean oldValue = _keepAspectRatio;
418 _keepAspectRatio = keepAspectRatio;
419 firePropertyChange(PROPERTY_KEEP_ASPECT_RATIO, oldValue, keepAspectRatio);
420 }
421
422
423
424
425
426
427
428 public boolean isKeepAspectRatio() {
429 return _keepAspectRatio;
430 }
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449 public void setLiveTransfer(boolean aLive) {
450 _isLiveTransfer = aLive;
451 }
452
453
454
455
456
457
458
459 public void resetForReceiving() {
460 _imageCounter.reset();
461 }
462
463
464
465
466
467
468
469
470
471
472
473 public void updateValue(IMAGE ti) {
474 _timage = ti;
475 long cameraPortId = ti.getSourceHeader().cameraPortId;
476 if (cameraPortId != _lastCameraPortId) {
477 if (cameraPortId != IMAGE.DEFAULT_CAMERA_PORT_ID) {
478 if (_lastCameraPortId != UNDEFINED_CONDITION)
479 _imageCounter.reset();
480 } else
481 System.out.println("WARN: value of camera port ID: " + cameraPortId);
482 _lastCameraPortId = cameraPortId;
483 }
484
485 FrameHeader frmHdr = ti.getFrameHeader();
486
487
488 _imageCounter.calculate(frmHdr.frameNumber);
489
490
491 boolean isGrayscale = (frmHdr.imageFormat == 0 || frmHdr.imageFormat == 10 || (frmHdr.imageFormat == 5 && frmHdr.bytesPerPixel == 1));
492 if (isGrayscale) {
493 if (_clut == null || frmHdr.effectiveBitsPerPixel != _clut.getPixelSize()) {
494 ImageCLUT clut = new ImageCLUT(_clut == null ? ColorMap.GRAYSCALE : _clut.getColorMap(),
495 new int[] { frmHdr.effectiveBitsPerPixel });
496 setCLUT(clut);
497 setHistogramMin(0);
498 setHistogramMax(clut.getMaxValue());
499
500 } else if (ColorMap.NONE.equals(_clut.getColorMap()))
501 setCLUT(new ImageCLUT(ColorMap.GRAYSCALE, new int[] { frmHdr.effectiveBitsPerPixel }));
502
503 } else if (_clut == null || !ColorMap.NONE.equals(_clut.getColorMap()) || !_clut.isRGB()) {
504 ImageCLUT clut = new ImageCLUT(ColorMap.NONE, new int[] { 8, 8, 8 });
505 setCLUT(clut);
506 setHistogramMin(0);
507 setHistogramMax(clut.getMaxValue());
508 }
509
510
511 Runnable r = new Runnable() {
512 public void run() {
513 createBufferedImage();
514 }
515 };
516
517 if (SwingUtilities.isEventDispatchThread()) {
518 r.run();
519 } else {
520 SwingUtilities.invokeLater(r);
521 }
522 }
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542 public BufferedImage getSnapshotImage() {
543 return _image;
544 }
545
546
547
548
549
550
551
552
553
554 public boolean saveAsPNGImage(String fileNamePath) {
555 synchronized (_lockHeaderUpdate) {
556 try {
557 return TImageIO.write(new TBufferedImage(_timage), new File(fileNamePath));
558
559 } catch (Exception e) {
560 getExceptionListener().exceptionThrown(e);
561 }
562 return false;
563 }
564 }
565
566
567
568
569
570
571
572
573
574 public boolean exportToPNG(String fileNamePath) {
575 synchronized (_lockHeaderUpdate) {
576 try {
577
578
579
580
581
582
583 return TImageIO.write(new TBufferedImage(TBufferedImage.toImageRGB(_image), null),
584 new File(fileNamePath));
585
586
587
588
589
590
591
592
593 } catch (Exception e) {
594 getExceptionListener().exceptionThrown(e);
595 }
596 return false;
597 }
598 }
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615 private void initialize() {
616
617 setLayout(new BorderLayout());
618
619
620
621 addMouseListener(new MouseListener() {
622 @Override
623 public void mouseClicked(MouseEvent e) {
624
625 }
626
627 @Override
628 public void mouseEntered(MouseEvent e) {
629 _isMouseInCanvas = true;
630 if (_overlayState == OverlayState.AUTO)
631 repaintImage();
632 }
633
634 @Override
635 public void mouseExited(MouseEvent e) {
636 _isMouseInCanvas = false;
637 if (_overlayState == OverlayState.AUTO)
638 repaintImage();
639 }
640
641 @Override
642 public void mousePressed(MouseEvent e) {
643 _isMousePressed = true;
644 if (_overlayState == OverlayState.ON || _overlayState == OverlayState.AUTO)
645 repaintImage();
646 }
647
648 @Override
649 public void mouseReleased(MouseEvent e) {
650 _isMousePressed = false;
651 if (_overlayState == OverlayState.ON || _overlayState == OverlayState.AUTO)
652 repaintImage();
653 }
654
655 });
656
657 addMouseMotionListener(new MouseMotionListener() {
658 @Override
659 public void mouseDragged(MouseEvent e) {
660 if (_overlayState == OverlayState.ON || _overlayState == OverlayState.AUTO)
661 repaintImage();
662 }
663
664 @Override
665 public void mouseMoved(MouseEvent e) {
666 }
667 });
668
669 addPropertyChangeListener(new PropertyChangeListener() {
670 public void propertyChange(PropertyChangeEvent e) {
671 String propertyChanged = e.getPropertyName();
672 if (propertyChanged.equals(PROPERTY_KEEP_ASPECT_RATIO)
673 || propertyChanged.equals(PROPERTY_OVERLAY_STATE)) {
674 repaintImage();
675
676 } else if (propertyChanged.equals(PROPERTY_IMAGE_ZOOM)) {
677 setPreferredSize(ImageZoom.AUTO.equals(_imageZoom) ? new Dimension(0, 0) : getZoomedSize(_image
678 .getWidth(), _image.getHeight(), getWidth(), getHeight()));
679 revalidate();
680 repaintImage();
681
682 } else if (propertyChanged.equals(PROPERTY_AOI_ZOOM)
683 || propertyChanged.equals(PROPERTY_HISTOGRAM_EQUALISATION)
684 || propertyChanged.equals(PROPERTY_HISTOGRAM_MIN)
685 || propertyChanged.equals(PROPERTY_HISTOGRAM_MAX)
686 || propertyChanged.equals(PROPERTY_COLOR_MAP)) {
687 createBufferedImage();
688 }
689 }
690 });
691
692 createSplashScreen(_timage = new IMAGE(0));
693 updateValue(_timage);
694 }
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709 @SuppressWarnings("deprecation")
710 private void createSplashScreen(IMAGE destImage) {
711 if (destImage == null) {
712 getExceptionListener().exceptionThrown(new NullPointerException("destImage == null!"));
713 return;
714 }
715
716 String strURL = System.getProperty("splashImage.url");
717 URL splashURL = null;
718 try {
719 if (strURL == null || (strURL = strURL.trim()).isEmpty())
720 splashURL = (new File("splash.png")).toURI().toURL();
721 else
722 splashURL = new URL(strURL);
723
724 } catch (MalformedURLException e) {
725 getExceptionListener().exceptionThrown(e);
726 return;
727 }
728
729
730 if (!ImageParser.loadImageFile(splashURL, destImage, false) && !ImageParser.loadIMM("splash.imm", destImage)) {
731
732 IMAGE.FrameHeader frameHeader = destImage.getFrameHeader();
733 IMAGE.SourceHeader sourceHeader = destImage.getSourceHeader();
734
735
736 frameHeader.bytesPerPixel = 1;
737 frameHeader.appendedFrameSize = 768 * 576 * frameHeader.bytesPerPixel;
738 frameHeader.effectiveBitsPerPixel = 8;
739
740
741 frameHeader.imageFlags |= ImageFlag.IMAGE_LOSSLESS.getId();
742
743 frameHeader.imageFormat = frameHeader.sourceFormat = ImageFormat.IMAGE_FORMAT_GRAY.getId();
744 frameHeader.sourceHeight = 576;
745 frameHeader.sourceWidth = 768;
746 frameHeader.xScale = frameHeader.yScale = 1F;
747
748
749 sourceHeader.cameraPortName = "Internal Backup Splashscreen";
750 java.util.Calendar c = java.util.Calendar.getInstance();
751 sourceHeader.timestampMicroseconds = (int) (((c.getTimeInMillis() % ((long) 1000)) * ((long) 1000)));
752 sourceHeader.timestampSeconds = (int) (((c.getTimeInMillis()) / ((long) 1000)));
753 sourceHeader.totalLength = IMAGE.HEADER_SIZE + frameHeader.appendedFrameSize;
754
755 byte[] buf = new byte[frameHeader.appendedFrameSize];
756
757
758 for (int i = 0; i < frameHeader.sourceWidth * frameHeader.sourceHeight;)
759 buf[i++] = (byte) 0;
760
761 destImage.setImageFrameBuffer(buf);
762 }
763
764 }
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782 @SuppressWarnings("deprecation")
783 private void createErrorScreen(IMAGE destImage) {
784 if (destImage == null) {
785 getExceptionListener().exceptionThrown(new NullPointerException("destImage == null!"));
786 return;
787 }
788
789 String strURL = System.getProperty("errorImage.url");
790 URL splashURL = null;
791 try {
792 if (strURL == null || (strURL = strURL.trim()).isEmpty())
793 splashURL = (new File("error.png")).toURI().toURL();
794 else
795 splashURL = new URL(strURL);
796
797 } catch (MalformedURLException e) {
798 getExceptionListener().exceptionThrown(e);
799 return;
800 }
801
802 if (!ImageParser.loadImageFile(splashURL, destImage, false)) {
803
804
805 IMAGE.FrameHeader frameHeader = destImage.getFrameHeader();
806 IMAGE.SourceHeader sourceHeader = destImage.getSourceHeader();
807
808
809 frameHeader.appendedFrameSize = 768 * 576;
810 frameHeader.bytesPerPixel = 1;
811 frameHeader.effectiveBitsPerPixel = 8;
812
813
814 frameHeader.imageFlags |= ImageFlag.IMAGE_LOSSLESS.getId();
815
816 frameHeader.imageFormat = ImageFormat.IMAGE_FORMAT_GRAY.getId();
817 frameHeader.sourceFormat = ImageFormat.IMAGE_FORMAT_GRAY.getId();
818 frameHeader.sourceHeight = 576;
819 frameHeader.sourceWidth = 768;
820 frameHeader.xScale = frameHeader.yScale = 1F;
821
822
823 sourceHeader.cameraPortName = INVALID_CAMERA_PORT_NAME;
824 java.util.Calendar c = java.util.Calendar.getInstance();
825 sourceHeader.timestampMicroseconds = (int) (((c.getTimeInMillis() % ((long) 1000)) * ((long) 1000)));
826 sourceHeader.timestampSeconds = (int) (((c.getTimeInMillis()) / ((long) 1000)));
827 sourceHeader.totalLength = IMAGE.HEADER_SIZE + frameHeader.appendedFrameSize;
828
829
830
831 byte[] buf = new byte[frameHeader.appendedFrameSize];
832
833
834 for (int i = 0; i < frameHeader.sourceWidth * frameHeader.sourceHeight;)
835 buf[i++] = (byte) (Math.random() * 256.0);
836
837 destImage.setImageFrameBuffer(buf);
838 }
839 }
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854 private Dimension getZoomedSize(int srcW, int srcH, int destW, int destH) {
855 switch (_imageZoom) {
856 case AUTO:
857 return new Dimension(destW, destH);
858
859 case HALF:
860 return new Dimension(srcW / 2, srcH / 2);
861
862 case NORMAL:
863 return new Dimension(srcW, srcH);
864
865 case DOUBLE:
866 return new Dimension(srcW * 2, srcH * 2);
867
868 default:
869 throw new IllegalStateException("unsupported image zoom: " + _imageZoom);
870 }
871 }
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886 private Dimension getKeepedRatioSize(int srcW, int srcH, int destW, int destH) {
887 float ratio = (float) srcW / (float) srcH;
888 if (destW > destH) {
889 int tmp = Math.round(ratio * destH);
890 if (tmp > destW)
891 return new Dimension(destW, Math.round(destW / ratio));
892 return new Dimension(tmp, destH);
893 }
894 return new Dimension(destW, Math.round(destW / ratio));
895 }
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910 private void drawFrame(Graphics2D g2d, BufferedImage srcImage) {
911
912 int srcW = srcImage.getWidth();
913 int srcH = srcImage.getHeight();
914 int winW = getWidth();
915 int winH = getHeight();
916
917 Dimension destDim = (ImageZoom.AUTO.equals(_imageZoom) && _keepAspectRatio) ? getKeepedRatioSize(srcW, srcH,
918 winW, winH) : getZoomedSize(srcW, srcH, winW, winH);
919 Point destPoint = new Point((winW - destDim.width) / 2, (winH - destDim.height) / 2);
920
921 g2d.drawImage(srcImage, destPoint.x, destPoint.y, destDim.width, destDim.height, null);
922
923 g2d.setColor(Color.gray);
924 g2d.drawRect(destPoint.x - 1, destPoint.y - 1, destDim.width + 2, destDim.height + 2);
925
926 int xStart = (Integer) srcImage.getProperty(TImageMetadata.KEY_XSTART);
927 int yStart = (Integer) srcImage.getProperty(TImageMetadata.KEY_YSTART);
928 int aoiW = (Integer) srcImage.getProperty(TImageMetadata.KEY_AOI_WIDTH);
929 int aoiH = (Integer) srcImage.getProperty(TImageMetadata.KEY_AOI_HEIGHT);
930
931 String cameraPortName = (String) srcImage.getProperty(TImageMetadata.KEY_CAMERA_PORT_NAME);
932 long frameNumber = (Long) srcImage.getProperty(TImageMetadata.KEY_FRAME_NUMBER);
933 int bits = (Integer) srcImage.getProperty(TImageMetadata.KEY_EFFECTIVE_BITS_PER_PIXEL);
934 int bpp = (Integer) srcImage.getProperty(TImageMetadata.KEY_BYTES_PER_PIXEL);
935 int srcFormat = (Integer) srcImage.getProperty(TImageMetadata.KEY_SOURCE_FORMAT);
936 int imgFormat = (Integer) srcImage.getProperty(TImageMetadata.KEY_IMAGE_FORMAT);
937 int imgFlags = (Integer) srcImage.getProperty(TImageMetadata.KEY_IMAGE_FLAGS);
938 float imgRotation = (Float) srcImage.getProperty(TImageMetadata.KEY_IMAGE_ROTATION);
939 int timestampSeconds = (Integer) srcImage.getProperty(TImageMetadata.KEY_TIMESTAMP_SECONDS);
940 int timestampMicroseconds = (Integer) srcImage.getProperty(TImageMetadata.KEY_TIMESTAMP_MICROSECONDS);
941
942 if (cameraPortName == null || INVALID_CAMERA_PORT_NAME.equals(cameraPortName))
943 drawErrorString(g2d, frameNumber, winW, winH);
944
945 boolean isAoi = (aoiW != UNDEFINED_CONDITION);
946
947
948 if (OverlayState.ON.equals(_overlayState) || (_isMouseInCanvas && OverlayState.AUTO.equals(_overlayState))) {
949 StringBuilder sb = new StringBuilder();
950 if (_isMousePressed) {
951
952
953
954
955
956
957
958
959 Rectangle destRect = new Rectangle(destPoint.x, destPoint.y, destDim.width, destDim.height);
960
961 Point p = getMousePosition();
962 if (p != null && destRect.contains(p)) {
963 double scaleX = 1D * srcW / destRect.width;
964 double scaleY = 1D * srcH / destRect.height;
965 int pX = (int) (scaleX * (p.x - destRect.x));
966 int pY = (int) (scaleY * (p.y - destRect.y));
967 int rgb = ((BufferedImage) _image).getRGB(pX, pY);
968 sb.append(" px(").append(pX).append(",").append(pY).append(") = ");
969 sb.append("(").append((rgb >> 16) & 0xff);
970 sb.append("/").append((rgb >> 8) & 0xff);
971 sb.append("/").append(rgb & 0xff).append(")");
972 } else
973 sb.append(" (out of dimension)");
974 }
975
976 final int ovH = 36;
977 final int insetX = 10;
978 final int insetY = 15;
979
980 Rectangle r = getVisibleRect();
981
982
983 g2d.setColor(Color.lightGray);
984 g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.85F));
985 g2d.fillRect(r.x, r.y, r.width, ovH);
986 g2d.fillRect(r.x, r.y + r.height - ovH, r.width, ovH);
987
988 g2d.setColor(Color.black);
989
990 FontMetrics fm = g2d.getFontMetrics();
991
992 String leftStr = "Source: " + cameraPortName + " (" + ImageFormat.valueOf(srcFormat) + ")";
993 String rightStr = "# " + frameNumber + " - "
994 + formatDate(new Date((timestampSeconds * 1000L) + (timestampMicroseconds / 1000L)));
995
996 int strW = fm.stringWidth(leftStr) + insetX;
997 drawOverlayStr(leftStr, g2d, fm, r.x, r.y + insetY, r.x + r.width, ovH, false);
998 drawOverlayStr(rightStr, g2d, fm, r.x + strW, r.y + insetY, r.x + r.width, ovH, true);
999
1000 leftStr = "Size: " + srcW + " px * " + srcH + " px * " + bits + " of " + bpp * 8 + " bpp ";
1001 if (isAoi)
1002 leftStr += "(AOI: L " + xStart + ", T " + yStart + ", W " + aoiW + ", H " + aoiH + ")";
1003 else
1004 leftStr += "(AOI: none)";
1005
1006 rightStr = "Drop: " + _imageCounter.getDroppedFrames()
1007 + " (" + String.format("%4.3f", _imageCounter.getDroppedPercent() * 100D) + "%)";
1008
1009 strW = fm.stringWidth(leftStr) + insetX;
1010 drawOverlayStr(leftStr, g2d, fm, r.x, r.y + 2 * insetY, r.x + r.width, ovH, false);
1011 drawOverlayStr(rightStr, g2d, fm, r.x + strW, r.y + 2 * insetY, r.x + r.width, ovH, true);
1012
1013 leftStr = "Format: " + ImageFormat.valueOf(imgFormat) + sb.toString();
1014 strW = fm.stringWidth(leftStr) + insetX;
1015 drawOverlayStr(leftStr, g2d, fm, r.x, r.y + r.height - ovH + insetY, r.x + r.width, ovH, false);
1016
1017 rightStr = "Rotation: " + String.format("%4.2f", Math.abs(imgRotation)) + "°";
1018 if (imgRotation > 0F)
1019 rightStr += " cw";
1020 else if (imgRotation < 0F)
1021 rightStr += " ccw";
1022
1023 leftStr = "Flags: " + ImageFlag.toString(imgFlags);
1024 strW = fm.stringWidth(leftStr) + insetX;
1025
1026 drawOverlayStr(leftStr, g2d, fm, r.x, r.y + r.height - ovH + 2 * insetY, r.x + r.width, ovH, false);
1027 drawOverlayStr(rightStr, g2d, fm, r.x + strW, r.y + r.height - ovH + 2 * insetY, r.x + r.width, ovH, true);
1028 }
1029 }
1030
1031 private void drawErrorString(Graphics2D g2d, long frameNumber, int w, int h) {
1032
1033 String s1 = "Error: Video Frame #" + frameNumber + " could not be decoded or displayed properly.";
1034
1035 Font orgFont = g2d.getFont();
1036 g2d.setFont(orgFont.deriveFont(Font.BOLD, (float) 12.0));
1037
1038 int fontH = g2d.getFontMetrics().getHeight();
1039 int maxH = fontH + 20;
1040
1041 int s1w = g2d.getFontMetrics().stringWidth(s1);
1042 int maxW = s1w + 40;
1043
1044 int y = (h - maxH) / 2;
1045
1046 g2d.setColor(Color.black);
1047 g2d.fillRect((w - maxW) / 2, y, maxW, maxH);
1048
1049 g2d.setColor(Color.red);
1050 g2d.drawString(s1, (w - s1w) / 2, y + 20);
1051
1052 g2d.setFont(orgFont);
1053 }
1054
1055
1056
1057
1058 private void drawOverlayStr(String str,
1059 Graphics2D g2d,
1060 FontMetrics fm,
1061 int x,
1062 int y,
1063 int width,
1064 int height,
1065 boolean isRight) {
1066
1067 if (str == null || str.length() == 0)
1068 return;
1069
1070 int insetX = 10;
1071
1072 int area = width - x;
1073 int stringWidth = fm.stringWidth(str) + insetX;
1074
1075 if (stringWidth + insetX > area) {
1076 String suffix = "...";
1077 int suffixWidth = fm.stringWidth(suffix) + insetX;
1078 stringWidth = suffixWidth + insetX;
1079
1080 StringBuilder sb = new StringBuilder();
1081 for (int i = 0; i < str.length(); i++) {
1082 if (stringWidth >= area)
1083 break;
1084 sb.append(str.charAt(i));
1085 stringWidth = insetX + fm.stringWidth(sb.toString()) + suffixWidth;
1086 }
1087 g2d.drawString(sb.append(suffix).toString(), x + insetX, y);
1088
1089 } else
1090 g2d.drawString(str, (isRight ? width - stringWidth : x + insetX), y);
1091 }
1092
1093
1094
1095
1096
1097
1098 private void createBufferedImage() {
1099
1100 try {
1101
1102 _image = TBufferedImage.toBufferedImage(_timage, _clut.getColorMap(), _histogramEqualisation,
1103 _histogramMin, _histogramMax, !_aoiZoom);
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121 } catch (Exception e) {
1122 _image = TBufferedImage.toBufferedImage(_timage = getErrorScreen());
1123 getExceptionListener().exceptionThrown(e);
1124 }
1125
1126
1127 repaint();
1128 }
1129
1130
1131
1132
1133 private void repaintImage() {
1134 if (_isLiveTransfer) {
1135 if (_timer == null) {
1136 _timer = new Timer(TIMER_DELAY, new ActionListener() {
1137 public void actionPerformed(ActionEvent evt) {
1138 repaint();
1139 }
1140 });
1141 _timer.start();
1142 } else
1143 _timer.restart();
1144 } else
1145 repaint();
1146 }
1147
1148
1149
1150
1151
1152
1153
1154 private String formatDate(Date date) {
1155 java.util.Calendar tmp = java.util.Calendar.getInstance();
1156 int day = tmp.get(java.util.Calendar.DAY_OF_YEAR);
1157 int year = tmp.get(java.util.Calendar.YEAR);
1158 tmp.setTime(date);
1159 boolean isSameDate = (day == tmp.get(Calendar.DAY_OF_YEAR) && year == tmp.get(Calendar.YEAR));
1160 return new SimpleDateFormat(isSameDate ? "HH:mm:ss.SSS" : "dd.MM.yy HH:mm:ss.SSS").format(date);
1161 }
1162
1163
1164
1165
1166
1167
1168
1169 private IMAGE getErrorScreen() {
1170 if (_errorScreen == null) {
1171 _errorScreen = new IMAGE(0);
1172 createErrorScreen(_errorScreen);
1173 }
1174 return _errorScreen;
1175 }
1176
1177
1178
1179
1180
1181
1182
1183 public void setExceptionListener(ExceptionListener exceptionListener) {
1184 this._exceptionListener = exceptionListener;
1185 }
1186
1187
1188
1189
1190
1191
1192 public ExceptionListener getExceptionListener() {
1193 return (_exceptionListener != null) ? _exceptionListener : defaultExceptionListener;
1194 }
1195
1196
1197
1198
1199
1200
1201 private static final ExceptionListener defaultExceptionListener = new ExceptionListener() {
1202 public void exceptionThrown(Exception e) {
1203 System.err.println(e);
1204 }
1205 };
1206 }