Download Jar. Uses 6490K of libraries. Click here for Pure Java version (10470L/73K).
1 | !7 |
2 | |
3 | import static java.awt.RenderingHints.KEY_ANTIALIASING; |
4 | import static java.awt.RenderingHints.KEY_INTERPOLATION; |
5 | import static java.awt.RenderingHints.KEY_RENDERING; |
6 | import static java.awt.RenderingHints.VALUE_ANTIALIAS_OFF; |
7 | import static java.awt.RenderingHints.VALUE_ANTIALIAS_ON; |
8 | import static java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR; |
9 | import static java.awt.RenderingHints.VALUE_RENDER_SPEED; |
10 | |
11 | import java.beans.PropertyChangeEvent; |
12 | import java.beans.PropertyChangeListener; |
13 | import org.slf4j.Logger; |
14 | import org.slf4j.LoggerFactory; |
15 | |
16 | static BufferedImage imageFromWebcamPanel(WebcamPanel2 panel) { |
17 | ret panel.image; |
18 | } |
19 | /** |
20 | * Simply implementation of JPanel allowing users to render pictures taken with webcam. |
21 | * |
22 | * @author Bartosz Firyn (SarXos) |
23 | */ |
24 | sclass WebcamPanel2 extends JPanel implements WebcamListener, PropertyChangeListener { |
25 | |
26 | /** |
27 | * This enum is to control of how image will be drawn in the panel bounds. |
28 | * |
29 | * @author Sylwia Kauczor |
30 | */ |
31 | public enum DrawMode { |
32 | |
33 | /** |
34 | * Do not resize image - paint it as it is. This will make the image to go off out the |
35 | * bounds if panel is smaller than image size. |
36 | */ |
37 | NONE, |
38 | |
39 | /** |
40 | * Will resize image to the panel bounds. This mode does not care of the image scale, so the |
41 | * final image may be disrupted. |
42 | */ |
43 | FILL, |
44 | |
45 | /** |
46 | * Will fir image into the panel bounds. This will resize the image and keep both x and y |
47 | * scale factor. |
48 | */ |
49 | FIT, |
50 | } |
51 | |
52 | public interface ImageSupplier { |
53 | public BufferedImage get(); |
54 | } |
55 | |
56 | private static class DefaultImageSupplier implements ImageSupplier { |
57 | |
58 | private final Webcam webcam; |
59 | |
60 | public DefaultImageSupplier(Webcam webcam) { |
61 | this.webcam = webcam; |
62 | } |
63 | |
64 | @Override |
65 | public BufferedImage get() { |
66 | return webcam.getImage(); |
67 | } |
68 | } |
69 | |
70 | /** |
71 | * Interface of the painter used to draw image in panel. |
72 | * |
73 | * @author Bartosz Firyn (SarXos) |
74 | */ |
75 | public interface Painter { |
76 | |
77 | /** |
78 | * Paint panel without image. |
79 | * |
80 | * @param panel the webcam panel to paint on |
81 | * @param g2 the graphics 2D object used for drawing |
82 | */ |
83 | void paintPanel(WebcamPanel2 panel, Graphics2D g2); |
84 | |
85 | /** |
86 | * Paint webcam image in panel. |
87 | * |
88 | * @param panel the webcam panel to paint on |
89 | * @param image the image from webcam |
90 | * @param g2 the graphics 2D object used for drawing |
91 | */ |
92 | void paintImage(WebcamPanel2 panel, BufferedImage image, Graphics2D g2); |
93 | } |
94 | |
95 | /** |
96 | * Default painter used to draw image in panel. |
97 | * |
98 | * @author Bartosz Firyn (SarXos) |
99 | * @author Sylwia Kauczor |
100 | */ |
101 | public class DefaultPainter implements Painter { |
102 | |
103 | /** |
104 | * Webcam device name. |
105 | */ |
106 | private String name = null; |
107 | |
108 | /** |
109 | * Lat repaint time, uset for debug purpose. |
110 | */ |
111 | private long lastRepaintTime = -1; |
112 | |
113 | /** |
114 | * Buffered image resized to fit into panel drawing area. |
115 | */ |
116 | private BufferedImage resizedImage = null; |
117 | |
118 | @Override |
119 | public void paintPanel(WebcamPanel2 owner, Graphics2D g2) { |
120 | |
121 | assert owner != null; |
122 | assert g2 != null; |
123 | |
124 | Object antialiasing = g2.getRenderingHint(KEY_ANTIALIASING); |
125 | |
126 | g2.setRenderingHint(KEY_ANTIALIASING, isAntialiasingEnabled() ? VALUE_ANTIALIAS_ON : VALUE_ANTIALIAS_OFF); |
127 | g2.setBackground(Color.BLACK); |
128 | g2.fillRect(0, 0, getWidth(), getHeight()); |
129 | |
130 | int cx = (getWidth() - 70) / 2; |
131 | int cy = (getHeight() - 40) / 2; |
132 | |
133 | g2.setStroke(new BasicStroke(2)); |
134 | g2.setColor(Color.LIGHT_GRAY); |
135 | g2.fillRoundRect(cx, cy, 70, 40, 10, 10); |
136 | g2.setColor(Color.WHITE); |
137 | g2.fillOval(cx + 5, cy + 5, 30, 30); |
138 | g2.setColor(Color.LIGHT_GRAY); |
139 | g2.fillOval(cx + 10, cy + 10, 20, 20); |
140 | g2.setColor(Color.WHITE); |
141 | g2.fillOval(cx + 12, cy + 12, 16, 16); |
142 | g2.fillRoundRect(cx + 50, cy + 5, 15, 10, 5, 5); |
143 | g2.fillRect(cx + 63, cy + 25, 7, 2); |
144 | g2.fillRect(cx + 63, cy + 28, 7, 2); |
145 | g2.fillRect(cx + 63, cy + 31, 7, 2); |
146 | |
147 | g2.setColor(Color.DARK_GRAY); |
148 | g2.setStroke(new BasicStroke(3)); |
149 | g2.drawLine(0, 0, getWidth(), getHeight()); |
150 | g2.drawLine(0, getHeight(), getWidth(), 0); |
151 | |
152 | String str; |
153 | |
154 | final String strInitDevice = rb.getString("INITIALIZING_DEVICE"); |
155 | final String strNoImage = rb.getString("NO_IMAGE"); |
156 | final String strDeviceError = rb.getString("DEVICE_ERROR"); |
157 | |
158 | if (errored) { |
159 | str = strDeviceError; |
160 | } else { |
161 | str = starting ? strInitDevice : strNoImage; |
162 | } |
163 | |
164 | FontMetrics metrics = g2.getFontMetrics(getFont()); |
165 | int w = metrics.stringWidth(str); |
166 | int h = metrics.getHeight(); |
167 | |
168 | int x = (getWidth() - w) / 2; |
169 | int y = cy - h; |
170 | |
171 | g2.setFont(getFont()); |
172 | g2.setColor(Color.WHITE); |
173 | g2.drawString(str, x, y); |
174 | |
175 | if (name == null) { |
176 | name = webcam.getName(); |
177 | } |
178 | |
179 | str = name; |
180 | |
181 | w = metrics.stringWidth(str); |
182 | h = metrics.getHeight(); |
183 | |
184 | g2.drawString(str, (getWidth() - w) / 2, cy - 2 * h); |
185 | g2.setRenderingHint(KEY_ANTIALIASING, antialiasing); |
186 | } |
187 | |
188 | @Override |
189 | public void paintImage(WebcamPanel2 owner, BufferedImage image, Graphics2D g2) { |
190 | |
191 | assert owner != null; |
192 | assert image != null; |
193 | assert g2 != null; |
194 | |
195 | int pw = getWidth(); |
196 | int ph = getHeight(); |
197 | int iw = image.getWidth(); |
198 | int ih = image.getHeight(); |
199 | |
200 | Object antialiasing = g2.getRenderingHint(KEY_ANTIALIASING); |
201 | Object rendering = g2.getRenderingHint(KEY_RENDERING); |
202 | |
203 | g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_OFF); |
204 | g2.setRenderingHint(KEY_RENDERING, VALUE_RENDER_SPEED); |
205 | g2.setBackground(Color.BLACK); |
206 | g2.setColor(Color.BLACK); |
207 | g2.fillRect(0, 0, pw, ph); |
208 | |
209 | // resized image position and size |
210 | int x = 0; |
211 | int y = 0; |
212 | int w = 0; |
213 | int h = 0; |
214 | |
215 | switch (drawMode) { |
216 | case NONE: |
217 | w = image.getWidth(); |
218 | h = image.getHeight(); |
219 | break; |
220 | case FILL: |
221 | w = pw; |
222 | h = ph; |
223 | break; |
224 | case FIT: |
225 | double s = Math.max((double) iw / pw, (double) ih / ph); |
226 | double niw = iw / s; |
227 | double nih = ih / s; |
228 | double dx = (pw - niw) / 2; |
229 | double dy = (ph - nih) / 2; |
230 | w = (int) niw; |
231 | h = (int) nih; |
232 | x = (int) dx; |
233 | y = (int) dy; |
234 | break; |
235 | } |
236 | |
237 | if (resizedImage != null) { |
238 | resizedImage.flush(); |
239 | } |
240 | |
241 | if (w == image.getWidth() && h == image.getHeight() && !mirrored) { |
242 | resizedImage = image; |
243 | } else { |
244 | |
245 | GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment(); |
246 | GraphicsConfiguration gc = genv.getDefaultScreenDevice().getDefaultConfiguration(); |
247 | |
248 | Graphics2D gr = null; |
249 | try { |
250 | |
251 | resizedImage = gc.createCompatibleImage(pw, ph); |
252 | gr = resizedImage.createGraphics(); |
253 | gr.setComposite(AlphaComposite.Src); |
254 | |
255 | for (Map.Entry<RenderingHints.Key, Object> hint : imageRenderingHints.entrySet()) { |
256 | gr.setRenderingHint(hint.getKey(), hint.getValue()); |
257 | } |
258 | |
259 | gr.setBackground(Color.BLACK); |
260 | gr.setColor(Color.BLACK); |
261 | gr.fillRect(0, 0, pw, ph); |
262 | |
263 | int sx1, sx2, sy1, sy2; // source rectangle coordinates |
264 | int dx1, dx2, dy1, dy2; // destination rectangle coordinates |
265 | |
266 | dx1 = x; |
267 | dy1 = y; |
268 | dx2 = x + w; |
269 | dy2 = y + h; |
270 | |
271 | if (mirrored) { |
272 | sx1 = iw; |
273 | sy1 = 0; |
274 | sx2 = 0; |
275 | sy2 = ih; |
276 | } else { |
277 | sx1 = 0; |
278 | sy1 = 0; |
279 | sx2 = iw; |
280 | sy2 = ih; |
281 | } |
282 | |
283 | gr.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, null); |
284 | |
285 | } finally { |
286 | if (gr != null) { |
287 | gr.dispose(); |
288 | } |
289 | } |
290 | } |
291 | |
292 | g2.drawImage(resizedImage, 0, 0, null); |
293 | |
294 | if (isFPSDisplayed()) { |
295 | |
296 | String str = String.format("FPS: %.1f", webcam.getFPS()); |
297 | |
298 | int sx = 5; |
299 | int sy = ph - 5; |
300 | |
301 | g2.setFont(getFont()); |
302 | g2.setColor(Color.BLACK); |
303 | g2.drawString(str, sx + 1, sy + 1); |
304 | g2.setColor(Color.WHITE); |
305 | g2.drawString(str, sx, sy); |
306 | } |
307 | |
308 | if (isImageSizeDisplayed()) { |
309 | |
310 | String res = String.format("%d\u2A2F%d px", iw, ih); |
311 | |
312 | FontMetrics metrics = g2.getFontMetrics(getFont()); |
313 | int sw = metrics.stringWidth(res); |
314 | int sx = pw - sw - 5; |
315 | int sy = ph - 5; |
316 | |
317 | g2.setFont(getFont()); |
318 | g2.setColor(Color.BLACK); |
319 | g2.drawString(res, sx + 1, sy + 1); |
320 | g2.setColor(Color.WHITE); |
321 | g2.drawString(res, sx, sy); |
322 | } |
323 | |
324 | if (isDisplayDebugInfo()) { |
325 | |
326 | if (lastRepaintTime < 0) { |
327 | lastRepaintTime = System.currentTimeMillis(); |
328 | } else { |
329 | |
330 | long now = System.currentTimeMillis(); |
331 | String res = String.format("DEBUG: repaints per second: %.1f", (double) 1000 / (now - lastRepaintTime)); |
332 | lastRepaintTime = now; |
333 | g2.setFont(getFont()); |
334 | g2.setColor(Color.BLACK); |
335 | g2.drawString(res, 6, 16); |
336 | g2.setColor(Color.WHITE); |
337 | g2.drawString(res, 5, 15); |
338 | } |
339 | } |
340 | |
341 | g2.setRenderingHint(KEY_ANTIALIASING, antialiasing); |
342 | g2.setRenderingHint(KEY_RENDERING, rendering); |
343 | } |
344 | } |
345 | |
346 | private static final class PanelThreadFactory implements ThreadFactory { |
347 | |
348 | private static final AtomicInteger number = new AtomicInteger(0); |
349 | |
350 | @Override |
351 | public Thread newThread(Runnable r) { |
352 | Thread t = new Thread(r, String.format("webcam-panel-scheduled-executor-%d", number.incrementAndGet())); |
353 | t.setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance()); |
354 | t.setDaemon(true); |
355 | return t; |
356 | } |
357 | } |
358 | |
359 | /** |
360 | * This runnable will do nothing more than repaint panel. |
361 | */ |
362 | private static final class SwingRepainter implements Runnable { |
363 | |
364 | private WebcamPanel2 panel = null; |
365 | |
366 | public SwingRepainter(WebcamPanel2 panel) { |
367 | this.panel = panel; |
368 | } |
369 | |
370 | @Override |
371 | public void run() { |
372 | panel.repaint(); |
373 | } |
374 | } |
375 | |
376 | private static final long serialVersionUID = 1L; |
377 | |
378 | private static final Logger LOG = LoggerFactory.getLogger(WebcamPanel2.class); |
379 | |
380 | public static final double MIN_FREQUENCY = 0.016; // 1 frame per minute |
381 | |
382 | private static final double MAX_FREQUENCY = 50; // 50 frames per second |
383 | |
384 | private static final ThreadFactory THREAD_FACTORY = new PanelThreadFactory(); |
385 | |
386 | public static final Map<RenderingHints.Key, Object> DEFAULT_IMAGE_RENDERING_HINTS = new HashMap<RenderingHints.Key, Object>(); |
387 | static { |
388 | DEFAULT_IMAGE_RENDERING_HINTS.put(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR); |
389 | DEFAULT_IMAGE_RENDERING_HINTS.put(KEY_RENDERING, VALUE_RENDER_SPEED); |
390 | DEFAULT_IMAGE_RENDERING_HINTS.put(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON); |
391 | } |
392 | |
393 | /** |
394 | * This runnable will do nothing more than repaint panel. |
395 | */ |
396 | private final Runnable repaint = new SwingRepainter(this); |
397 | |
398 | /** |
399 | * Rendering hints to be used when painting image to be displayed. |
400 | */ |
401 | private Map<RenderingHints.Key, Object> imageRenderingHints = new HashMap<RenderingHints.Key, Object>(DEFAULT_IMAGE_RENDERING_HINTS); |
402 | |
403 | /** |
404 | * Scheduled executor acting as timer. |
405 | */ |
406 | private ScheduledExecutorService executor = null; |
407 | |
408 | /** |
409 | * Image updater reads images from camera and force panel to be repainted. |
410 | * |
411 | * @author Bartosz Firyn (SarXos) |
412 | */ |
413 | private class ImageUpdater implements Runnable { |
414 | private class RepaintScheduler extends Thread { |
415 | public RepaintScheduler() { |
416 | setUncaughtExceptionHandler(WebcamExceptionHandler.getInstance()); |
417 | setName(String.format("repaint-scheduler-%s", webcam.getName())); |
418 | setDaemon(true); |
419 | } |
420 | |
421 | @Override |
422 | public void run() { |
423 | |
424 | // do nothing when not running |
425 | if (!running.get() || isInIconifiedFrame(WebcamPanel2.this)) { |
426 | _sleep(100); |
427 | return; |
428 | } |
429 | |
430 | repaintPanel(); |
431 | |
432 | // loop when starting, to wait for images |
433 | while (starting) { |
434 | try { |
435 | Thread.sleep(50); |
436 | } catch (InterruptedException e) { |
437 | throw new RuntimeException(e); |
438 | } |
439 | } |
440 | |
441 | // schedule update when webcam is open, otherwise schedule |
442 | // second scheduler execution |
443 | |
444 | try { |
445 | |
446 | // FPS limit means that panel rendering frequency is |
447 | // limited to the specific value and panel will not be |
448 | // rendered more often then specific value |
449 | |
450 | if (webcam.isOpen()) { |
451 | |
452 | // TODO: rename FPS value in panel to rendering |
453 | // frequency |
454 | |
455 | if (isFPSLimited()) { |
456 | executor.scheduleAtFixedRate(updater, 0, (long) (1000 / frequency), TimeUnit.MILLISECONDS); |
457 | } else { |
458 | executor.scheduleWithFixedDelay(updater, 100, 1, TimeUnit.MILLISECONDS); |
459 | } |
460 | } else { |
461 | executor.schedule(this, 500, TimeUnit.MILLISECONDS); |
462 | } |
463 | } catch (RejectedExecutionException e) { |
464 | |
465 | // executor has been shut down, which means that someone |
466 | // stopped panel / webcam device before it was actually |
467 | // completely started (it was in "starting" timeframe) |
468 | |
469 | LOG.warn("Executor rejected paint update"); |
470 | LOG.trace("Executor rejected paint update because of", e); |
471 | } |
472 | } |
473 | } |
474 | |
475 | /** |
476 | * Update scheduler thread. |
477 | */ |
478 | private Thread scheduler = null; |
479 | |
480 | /** |
481 | * Is repainter running? |
482 | */ |
483 | private AtomicBoolean running = new AtomicBoolean(false); |
484 | |
485 | /** |
486 | * Start repainter. Can be invoked many times, but only first call will take effect. |
487 | */ |
488 | public void start() { |
489 | if (running.compareAndSet(false, true)) { |
490 | executor = Executors.newScheduledThreadPool(1, THREAD_FACTORY); |
491 | scheduler = new RepaintScheduler(); |
492 | scheduler.start(); |
493 | } |
494 | } |
495 | |
496 | /** |
497 | * Stop repainter. Can be invoked many times, but only first call will take effect. |
498 | * |
499 | * @throws InterruptedException |
500 | */ |
501 | public void stop() throws InterruptedException { |
502 | if (running.compareAndSet(true, false)) { |
503 | executor.shutdown(); |
504 | executor.awaitTermination(5000, TimeUnit.MILLISECONDS); |
505 | scheduler.join(); |
506 | } |
507 | } |
508 | |
509 | @Override |
510 | public void run() { |
511 | try { |
512 | update(); |
513 | } catch (Throwable t) { |
514 | errored = true; |
515 | WebcamExceptionHandler.handle(t); |
516 | } |
517 | } |
518 | |
519 | /** |
520 | * Perform single panel area update (repaint newly obtained image). |
521 | */ |
522 | private void update() { |
523 | |
524 | // do nothing when updater not running, when webcam is closed, or |
525 | // panel repainting is paused |
526 | |
527 | if (!running.get() || !webcam.isOpen() || paused) { |
528 | return; |
529 | } |
530 | |
531 | // get new image from webcam |
532 | |
533 | BufferedImage tmp = supplier.get(); |
534 | boolean repaint = true; |
535 | |
536 | if (tmp != null) { |
537 | |
538 | // ignore repaint if image is the same as before |
539 | if (image == tmp) { |
540 | repaint = false; |
541 | } |
542 | |
543 | errored = false; |
544 | image = tmp; |
545 | } |
546 | |
547 | if (repaint) { |
548 | repaintPanel(); |
549 | } |
550 | } |
551 | } |
552 | |
553 | /** |
554 | * Resource bundle. |
555 | */ |
556 | private ResourceBundle rb = null; |
557 | |
558 | /** |
559 | * The mode of how the image will be resized to fit into panel bounds. Default is |
560 | * {@link DrawMode#FIT} |
561 | * |
562 | * @see DrawMode |
563 | */ |
564 | private DrawMode drawMode = DrawMode.FIT; |
565 | |
566 | /** |
567 | * Frames requesting frequency. |
568 | */ |
569 | private double frequency = 5; // FPS |
570 | |
571 | /** |
572 | * Is frames requesting frequency limited? If true, images will be fetched in configured time |
573 | * intervals. If false, images will be fetched as fast as camera can serve them. |
574 | */ |
575 | private boolean frequencyLimit = false; |
576 | |
577 | /** |
578 | * Display FPS. |
579 | */ |
580 | private boolean frequencyDisplayed = false; |
581 | |
582 | /** |
583 | * Display image size. |
584 | */ |
585 | private boolean imageSizeDisplayed = false; |
586 | |
587 | /** |
588 | * Is antialiasing enabled (true by default). |
589 | */ |
590 | private boolean antialiasingEnabled = true; |
591 | |
592 | /** |
593 | * Webcam object used to fetch images. |
594 | */ |
595 | private final Webcam webcam; |
596 | |
597 | private final ImageSupplier supplier; |
598 | |
599 | /** |
600 | * Repainter is used to fetch images from camera and force panel repaint when image is ready. |
601 | */ |
602 | private final ImageUpdater updater; |
603 | |
604 | /** |
605 | * Image currently being displayed. |
606 | */ |
607 | BufferedImage image = null; |
608 | |
609 | /** |
610 | * Webcam is currently starting. |
611 | */ |
612 | private volatile boolean starting = false; |
613 | |
614 | /** |
615 | * Painting is paused. |
616 | */ |
617 | private volatile boolean paused = false; |
618 | |
619 | /** |
620 | * Is there any problem with webcam? |
621 | */ |
622 | private volatile boolean errored = false; |
623 | |
624 | /** |
625 | * Webcam has been started. |
626 | */ |
627 | private final AtomicBoolean started = new AtomicBoolean(false); |
628 | |
629 | /** |
630 | * Default painter. |
631 | */ |
632 | private final Painter defaultPainter = new DefaultPainter(); |
633 | |
634 | /** |
635 | * Painter used to draw image in panel. |
636 | * |
637 | * @see #setPainter(Painter) |
638 | * @see #getPainter() |
639 | */ |
640 | private Painter painter = defaultPainter; |
641 | |
642 | /** |
643 | * Preferred panel size. |
644 | */ |
645 | private Dimension defaultSize = null; |
646 | |
647 | /** |
648 | * If debug info should be displayed. |
649 | */ |
650 | private boolean displayDebugInfo = false; |
651 | |
652 | /** |
653 | * Is image mirrored. |
654 | */ |
655 | private boolean mirrored = false; |
656 | |
657 | /** |
658 | * Creates webcam panel and automatically start webcam. |
659 | * |
660 | * @param webcam the webcam to be used to fetch images |
661 | */ |
662 | public WebcamPanel2(Webcam webcam) { |
663 | this(webcam, true); |
664 | } |
665 | |
666 | /** |
667 | * Creates new webcam panel which display image from camera in you your Swing application. |
668 | * |
669 | * @param webcam the webcam to be used to fetch images |
670 | * @param start true if webcam shall be automatically started |
671 | */ |
672 | public WebcamPanel2(Webcam webcam, boolean start) { |
673 | this(webcam, null, start); |
674 | } |
675 | |
676 | /** |
677 | * Creates new webcam panel which display image from camera in you your Swing application. If |
678 | * panel size argument is null, then image size will be used. If you would like to fill panel |
679 | * area with image even if its size is different, then you can use |
680 | * {@link WebcamPanel#setFillArea(boolean)} method to configure this. |
681 | * |
682 | * @param webcam the webcam to be used to fetch images |
683 | * @param size the size of panel |
684 | * @param start true if webcam shall be automatically started |
685 | * @see WebcamPanel#setFillArea(boolean) |
686 | */ |
687 | public WebcamPanel2(Webcam webcam, Dimension size, boolean start) { |
688 | this(webcam, size, start, new DefaultImageSupplier(webcam)); |
689 | } |
690 | |
691 | public WebcamPanel2(Webcam webcam, Dimension size, boolean start, ImageSupplier supplier) { |
692 | |
693 | if (webcam == null) { |
694 | throw new IllegalArgumentException(String.format("Webcam argument in %s constructor cannot be null!", getClass().getSimpleName())); |
695 | } |
696 | |
697 | this.defaultSize = size; |
698 | this.webcam = webcam; |
699 | this.updater = new ImageUpdater(); |
700 | this.supplier = supplier; |
701 | this.rb = WebcamUtils.loadRB(WebcamPanel.class, getLocale()); |
702 | |
703 | setDoubleBuffered(true); |
704 | |
705 | addPropertyChangeListener("locale", this); |
706 | |
707 | if (size == null) { |
708 | Dimension r = webcam.getViewSize(); |
709 | if (r == null) { |
710 | r = webcam.getViewSizes()[0]; |
711 | } |
712 | setPreferredSize(r); |
713 | } else { |
714 | setPreferredSize(size); |
715 | } |
716 | |
717 | if (start) { |
718 | start(); |
719 | } |
720 | } |
721 | |
722 | /** |
723 | * Set new painter. Painter is a class which pains image visible when |
724 | * |
725 | * @param painter the painter object to be set |
726 | */ |
727 | public void setPainter(Painter painter) { |
728 | this.painter = painter; |
729 | } |
730 | |
731 | /** |
732 | * Get painter used to draw image in webcam panel. |
733 | * |
734 | * @return Painter object |
735 | */ |
736 | public Painter getPainter() { |
737 | return painter; |
738 | } |
739 | |
740 | @Override |
741 | protected void paintComponent(Graphics g) { |
742 | |
743 | super.paintComponent(g); |
744 | |
745 | if (image == null) { |
746 | painter.paintPanel(this, (Graphics2D) g); |
747 | } else { |
748 | painter.paintImage(this, image, (Graphics2D) g); |
749 | } |
750 | } |
751 | |
752 | /** |
753 | * Open webcam and start rendering. |
754 | */ |
755 | public void start() { |
756 | |
757 | if (!started.compareAndSet(false, true)) { |
758 | return; |
759 | } |
760 | |
761 | webcam.addWebcamListener(this); |
762 | |
763 | LOG.debug("Starting panel rendering and trying to open attached webcam"); |
764 | |
765 | updater.start(); |
766 | |
767 | starting = true; |
768 | |
769 | final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { |
770 | |
771 | @Override |
772 | protected Void doInBackground() throws Exception { |
773 | |
774 | try { |
775 | if (!webcam.isOpen()) { |
776 | errored = !webcam.open(); |
777 | } |
778 | } catch (WebcamException e) { |
779 | errored = true; |
780 | throw e; |
781 | } finally { |
782 | starting = false; |
783 | repaintPanel(); |
784 | } |
785 | |
786 | return null; |
787 | } |
788 | }; |
789 | worker.execute(); |
790 | } |
791 | |
792 | /** |
793 | * Stop rendering and close webcam. |
794 | */ |
795 | public void stop() { |
796 | |
797 | if (!started.compareAndSet(true, false)) { |
798 | return; |
799 | } |
800 | |
801 | webcam.removeWebcamListener(this); |
802 | |
803 | LOG.debug("Stopping panel rendering and closing attached webcam"); |
804 | |
805 | try { |
806 | updater.stop(); |
807 | } catch (InterruptedException e) { |
808 | throw new RuntimeException(e); |
809 | } |
810 | |
811 | image = null; |
812 | |
813 | final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { |
814 | |
815 | @Override |
816 | protected Void doInBackground() throws Exception { |
817 | |
818 | try { |
819 | if (webcam.isOpen()) { |
820 | errored = !webcam.close(); |
821 | } |
822 | } catch (WebcamException e) { |
823 | errored = true; |
824 | throw e; |
825 | } finally { |
826 | repaintPanel(); |
827 | } |
828 | |
829 | return null; |
830 | } |
831 | }; |
832 | worker.execute(); |
833 | } |
834 | |
835 | /** |
836 | * Repaint panel in Swing asynchronous manner. |
837 | */ |
838 | private void repaintPanel() { |
839 | SwingUtilities.invokeLater(repaint); |
840 | } |
841 | |
842 | /** |
843 | * Pause rendering. |
844 | */ |
845 | public void pause() { |
846 | if (paused) { |
847 | return; |
848 | } |
849 | |
850 | LOG.debug("Pausing panel rendering"); |
851 | |
852 | paused = true; |
853 | } |
854 | |
855 | /** |
856 | * Resume rendering. |
857 | */ |
858 | public void resume() { |
859 | |
860 | if (!paused) { |
861 | return; |
862 | } |
863 | |
864 | LOG.debug("Resuming panel rendering"); |
865 | |
866 | paused = false; |
867 | } |
868 | |
869 | /** |
870 | * Is frequency limit enabled? |
871 | * |
872 | * @return True or false |
873 | */ |
874 | public boolean isFPSLimited() { |
875 | return frequencyLimit; |
876 | } |
877 | |
878 | /** |
879 | * Enable or disable frequency limit. Frequency limit should be used for <b>all IP cameras |
880 | * working in pull mode</b> (to save number of HTTP requests). If true, images will be fetched |
881 | * in configured time intervals. If false, images will be fetched as fast as camera can serve |
882 | * them. |
883 | * |
884 | * @param frequencyLimit true if limiting the frequency of image requests |
885 | */ |
886 | public void setFPSLimited(boolean frequencyLimit) { |
887 | this.frequencyLimit = frequencyLimit; |
888 | } |
889 | |
890 | /** |
891 | * Get rendering frequency in FPS (equivalent to Hz). |
892 | * |
893 | * @return Rendering frequency |
894 | */ |
895 | public double getFPSLimit() { |
896 | return frequency; |
897 | } |
898 | |
899 | /** |
900 | * Set rendering frequency (in Hz or FPS). Minimum frequency is 0.016 (1 frame per minute) and |
901 | * maximum is 25 (25 frames per second). |
902 | * |
903 | * @param fps the frequency |
904 | */ |
905 | public void setFPSLimit(double fps) { |
906 | if (fps > MAX_FREQUENCY) { |
907 | fps = MAX_FREQUENCY; |
908 | } |
909 | if (fps < MIN_FREQUENCY) { |
910 | fps = MIN_FREQUENCY; |
911 | } |
912 | this.frequency = fps; |
913 | } |
914 | |
915 | /** |
916 | * Is displaying of some debug information enabled. |
917 | * |
918 | * @return True if debug information are enabled, false otherwise |
919 | */ |
920 | public boolean isDisplayDebugInfo() { |
921 | return displayDebugInfo; |
922 | } |
923 | |
924 | /** |
925 | * Display some debug information on image surface. |
926 | * |
927 | * @param displayDebugInfo the value to control debug information |
928 | */ |
929 | public void setDisplayDebugInfo(boolean displayDebugInfo) { |
930 | this.displayDebugInfo = displayDebugInfo; |
931 | } |
932 | |
933 | /** |
934 | * This method return true in case if camera FPS is set to be displayed on panel surface. |
935 | * Default value returned is false. |
936 | * |
937 | * @return True if camera FPS is set to be displayed on panel surface |
938 | * @see #setFPSDisplayed(boolean) |
939 | */ |
940 | public boolean isFPSDisplayed() { |
941 | return frequencyDisplayed; |
942 | } |
943 | |
944 | /** |
945 | * This method is to control if camera FPS should be displayed on the webcam panel surface. |
946 | * |
947 | * @param displayed the value to control if camera FPS should be displayed |
948 | */ |
949 | public void setFPSDisplayed(boolean displayed) { |
950 | this.frequencyDisplayed = displayed; |
951 | } |
952 | |
953 | /** |
954 | * This method will return true in case when panel is configured to display image size. The |
955 | * string will be printed in the right bottom corner of the panel surface. |
956 | * |
957 | * @return True in case if panel is configured to display image size |
958 | */ |
959 | public boolean isImageSizeDisplayed() { |
960 | return imageSizeDisplayed; |
961 | } |
962 | |
963 | /** |
964 | * Configure panel to display camera image size to be displayed. |
965 | * |
966 | * @param imageSizeDisplayed if true the pixel dimensions are displayed over the image. |
967 | */ |
968 | public void setImageSizeDisplayed(boolean imageSizeDisplayed) { |
969 | this.imageSizeDisplayed = imageSizeDisplayed; |
970 | } |
971 | |
972 | /** |
973 | * Turn on/off antialiasing. |
974 | * |
975 | * @param antialiasing the true to enable, false to disable antialiasing |
976 | */ |
977 | public void setAntialiasingEnabled(boolean antialiasing) { |
978 | this.antialiasingEnabled = antialiasing; |
979 | } |
980 | |
981 | /** |
982 | * @return True is antialiasing is enabled, false otherwise |
983 | */ |
984 | public boolean isAntialiasingEnabled() { |
985 | return antialiasingEnabled; |
986 | } |
987 | |
988 | /** |
989 | * Is webcam panel repainting starting. |
990 | * |
991 | * @return True if panel is starting |
992 | */ |
993 | public boolean isStarting() { |
994 | return starting; |
995 | } |
996 | |
997 | /** |
998 | * Is webcam panel repainting started. |
999 | * |
1000 | * @return True if panel repainting has been started |
1001 | */ |
1002 | public boolean isStarted() { |
1003 | return started.get(); |
1004 | } |
1005 | |
1006 | /** |
1007 | * This method returns the current draw mode, mainly used by custom painters |
1008 | * |
1009 | * @return the current value of the {@link DrawMode} |
1010 | */ |
1011 | public DrawMode getDrawMode() { |
1012 | return this.drawMode; |
1013 | } |
1014 | |
1015 | /** |
1016 | * This method sets the drawmode |
1017 | * |
1018 | * @param drawMode the desired {@link DrawMode} |
1019 | */ |
1020 | public void setDrawMode(DrawMode drawMode) { |
1021 | this.drawMode = drawMode; |
1022 | } |
1023 | |
1024 | /** |
1025 | * Indicates whether the panel is in an error state |
1026 | * |
1027 | * @return true if the panel has an error present |
1028 | */ |
1029 | public boolean isErrored() { |
1030 | return errored; |
1031 | } |
1032 | |
1033 | /** |
1034 | * Hints for rendering, mainly used for custom painters |
1035 | * |
1036 | * @return the stored RenderingHints |
1037 | * @deprecated use {@link #getDrawMode()} instead. |
1038 | */ |
1039 | @Deprecated |
1040 | public Map<RenderingHints.Key, Object> getImageRenderingHints() { |
1041 | return imageRenderingHints; |
1042 | } |
1043 | |
1044 | @Deprecated |
1045 | public boolean isFitArea() { |
1046 | return drawMode == DrawMode.FIT; |
1047 | } |
1048 | |
1049 | /** |
1050 | * This method will change the mode of panel area painting so the image will be resized and will |
1051 | * keep scale factor to fit into drawable panel bounds. When set to false, the mode will be |
1052 | * reset to {@link DrawMode#NONE} so image will be drawn as it is. |
1053 | * |
1054 | * @param fitArea the fit area mode enabled or disabled |
1055 | * @deprecated use {@link #setDrawMode(DrawMode drawMode)} instead. |
1056 | */ |
1057 | @Deprecated |
1058 | public void setFitArea(boolean fitArea) { |
1059 | this.drawMode = fitArea ? DrawMode.FIT : DrawMode.NONE; |
1060 | } |
1061 | |
1062 | /** |
1063 | * Image will be resized to fill panel area if true. If false then image will be rendered as it |
1064 | * was obtained from webcam instance. |
1065 | * |
1066 | * @param fillArea shall image be resided to fill panel area |
1067 | * @deprecated use {@link #setDrawMode(DrawMode drawMode)} instead. |
1068 | */ |
1069 | @Deprecated |
1070 | public void setFillArea(boolean fillArea) { |
1071 | this.drawMode = fillArea ? DrawMode.FILL : DrawMode.NONE; |
1072 | } |
1073 | |
1074 | /** |
1075 | * Get value of fill area setting. Image will be resized to fill panel area if true. If false |
1076 | * then image will be rendered as it was obtained from webcam instance. |
1077 | * |
1078 | * @return True if image is being resized, false otherwise |
1079 | * @deprecated use {@link #getDrawMode()} instead. |
1080 | */ |
1081 | @Deprecated |
1082 | public boolean isFillArea() { |
1083 | return drawMode == DrawMode.FILL; |
1084 | } |
1085 | |
1086 | /** |
1087 | * Get default painter used to draw panel. |
1088 | * |
1089 | * @return Default painter |
1090 | */ |
1091 | public Painter getDefaultPainter() { |
1092 | return defaultPainter; |
1093 | } |
1094 | |
1095 | @Override |
1096 | public void propertyChange(PropertyChangeEvent evt) { |
1097 | Locale lc = (Locale) evt.getNewValue(); |
1098 | if (lc != null) { |
1099 | rb = WebcamUtils.loadRB(WebcamPanel.class, lc); |
1100 | } |
1101 | } |
1102 | |
1103 | @Override |
1104 | public void webcamOpen(WebcamEvent we) { |
1105 | |
1106 | // if default size has not been provided, then use the one from webcam |
1107 | // device (this will be current webcam resolution) |
1108 | |
1109 | if (defaultSize == null) { |
1110 | setPreferredSize(webcam.getViewSize()); |
1111 | } |
1112 | } |
1113 | |
1114 | @Override |
1115 | public void webcamClosed(WebcamEvent we) { |
1116 | stop(); |
1117 | } |
1118 | |
1119 | @Override |
1120 | public void webcamDisposed(WebcamEvent we) { |
1121 | stop(); |
1122 | } |
1123 | |
1124 | @Override |
1125 | public void webcamImageObtained(WebcamEvent we) { |
1126 | // do nothing |
1127 | } |
1128 | |
1129 | /** |
1130 | * This method returns true if image mirroring is enabled. The default value is false. |
1131 | * |
1132 | * @return True if image is mirrored, false otherwise |
1133 | */ |
1134 | public boolean isMirrored() { |
1135 | return mirrored; |
1136 | } |
1137 | |
1138 | /** |
1139 | * Decide whether or not the image from webcam painted on panel surface will be mirrored. The |
1140 | * image from camera itself is not modified. |
1141 | * |
1142 | * @param mirrored the parameter to control if image should be mirrored |
1143 | */ |
1144 | public void setMirrored(boolean mirrored) { |
1145 | this.mirrored = mirrored; |
1146 | } |
1147 | |
1148 | /** |
1149 | * Return {@link Webcam} used by this panel. |
1150 | * |
1151 | * @return {@link Webcam} |
1152 | */ |
1153 | public Webcam getWebcam() { |
1154 | return webcam; |
1155 | } |
1156 | |
1157 | /** |
1158 | * @return {@link BufferedImage} displayed on {@link WebcamPanel} |
1159 | */ |
1160 | public BufferedImage getImage() { |
1161 | return image; |
1162 | } |
1163 | } |
1164 | |
1165 | static WebcamPanel2 panel; |
1166 | static bool savingOn = true; |
1167 | static double saveEvery = 10.0; |
1168 | static volatile File latestSaved; |
1169 | |
1170 | p { |
1171 | restartWith128MBHeap(); |
1172 | outBuf(50000); |
1173 | substance(); |
1174 | selectWebCam(voidfunc(Webcam cam) { |
1175 | panel = new WebcamPanel2(cam, true); |
1176 | panel.setFPSDisplayed(true); |
1177 | frameIcon(#1009177, exitProgramOnFrameClose(alwaysOnTop(showPackedFrame("JavaX Live WebCam", panel))); |
1178 | bot("WebCam."); |
1179 | doEvery_daemon_now(saveEvery, f saveDefault); |
1180 | }); |
1181 | hideConsole(); |
1182 | } |
1183 | |
1184 | answer { |
1185 | if "save jpeg at *" { |
1186 | saveJPEG(newFile($1), imageFromWebcamPanel(panel)); |
1187 | ret "OK"; |
1188 | } |
1189 | if "latest file" |
1190 | ret latestSaved == null ? "Not saved yet" |
1191 | : "OK " + quote(f2s(latestSaved)); |
1192 | } |
1193 | |
1194 | svoid saveDefault { |
1195 | if (!savingOn) ret; |
1196 | File file = newFile(javaxDataDir(), "WebCam", ("webcam-" + dateWithSecondsForFile() + ".jpg"); |
1197 | saveJPEG(file, imageFromWebcamPanel(panel)); |
1198 | latestSaved = file; |
1199 | } |
Began life as a copy of #1013489
download show line numbers debug dex old transpilations
Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1014901 |
Snippet name: | JavaX Live WebCam - Show live webcam image in frame v5 [try to minimize CPU when iconified - program works, but doesn't make a difference to vanilla WebcamPanel] |
Eternal ID of this version: | #1014901/8 |
Text MD5: | 7750d67fc344e1dcaecf6d54d46f14bb |
Transpilation MD5: | 6c38f054e53b86af88e25be20646b691 |
Author: | stefan |
Category: | javax / desktop / camera |
Type: | JavaX source code (desktop) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2018-05-06 11:38:19 |
Source code size: | 29723 bytes / 1199 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 404 / 1017 |
Version history: | 7 change(s) |
Referenced in: | [show references] |