4 Commits

Author SHA1 Message Date
3eaf6f0303 fix
Signed-off-by: rattatwinko <seppmutterman@gmail.com>
2026-01-20 08:37:48 +01:00
11c5aa9115 Merge branch 'experimenting'
Signed-off-by: rattatwinko <seppmutterman@gmail.com>

# Conflicts:
#	src/main/java/io/swtc/Main.java
2026-01-20 08:28:10 +01:00
55474092e3 some more optimizing!
now runs much better with lower power systems as we refactored code to firstly rule out double invokelaters, and we optimized the capture loop iteslf!

and we introduced a ShowError class which will be refactored and used quite thoroughly!

Signed-off-by: rattatwinko <seppmutterman@gmail.com>
2026-01-20 08:25:26 +01:00
41fbf62757 just some theme changes, i like this one more 2026-01-18 22:10:37 +01:00
4 changed files with 127 additions and 115 deletions

View File

@@ -1,26 +1,22 @@
package io.swtc;
import javax.swing.*;
import io.swtc.proccessing.ui.ShowError;
public class Main {
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.windows.WindowsLookAndFeel"
);
} catch (Exception e) {
JOptionPane.showMessageDialog(
null,
"LaF not available",
"LaF-Warning",
JOptionPane.WARNING_MESSAGE
);
for (int i = 0; i < args.length; i++) {
System.out.println("Arg " + i + ": " + args[i]);
}
// For some reason we need to invoke Later for LaF to work!
SwingUtilities.invokeLater(() -> SwingCCTVManager.main(null));
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
ShowError.warning(null,"LaF Warn","LaF");
}
SwingCCTVManager.main(null);
}
}

View File

@@ -3,17 +3,19 @@ package io.swtc.proccessing;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
/**
* Enhanced CameraPanel with support for custom image processors
*/
public class CameraPanel extends JPanel {
private BufferedImage currentImage;
private BufferedImage processedImage;
// Custom processor for advanced effects
private volatile BufferedImage sourceImage;
private volatile BufferedImage processedImage;
private Function<BufferedImage, BufferedImage> imageProcessor;
private final AtomicBoolean repaintScheduled = new AtomicBoolean(false);
public CameraPanel() {
setBackground(Color.BLACK);
@@ -21,78 +23,47 @@ public class CameraPanel extends JPanel {
}
public void setImage(BufferedImage img) {
this.currentImage = img;
processImage();
repaint();
sourceImage = img;
if (repaintScheduled.compareAndSet(false, true)) {
SwingUtilities.invokeLater(() -> {
repaintScheduled.set(false);
repaint();
});
}
}
public void setImageProcessor(Function<BufferedImage, BufferedImage> processor) {
this.imageProcessor = processor;
processImage();
repaint();
}
private void processImage() {
if (currentImage == null) {
processedImage = null;
return;
}
BufferedImage result = currentImage;
// Apply basic transforms first
result = applyBasicEffects(result);
// Apply custom processor if set
if (imageProcessor != null) {
result = imageProcessor.apply(result);
}
processedImage = result;
}
private BufferedImage applyBasicEffects(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Handle mirror/flip/rotate
int srcX = x;
int srcY = y;
// Ensure coordinates are in bounds
srcX = Math.max(0, Math.min(width - 1, srcX));
srcY = Math.max(0, Math.min(height - 1, srcY));
int rgb = img.getRGB(srcX, srcY);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
result.setRGB(x, y, (r << 16) | (g << 8) | b);
}
}
return result;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (processedImage != null) {
Graphics2D g2d = (Graphics2D) g;
BufferedImage src = sourceImage;
if (src == null) return;
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
BufferedImage img = src;
g2d.drawImage(processedImage, 0, 0, getWidth(), getHeight(), null);
if (imageProcessor != null) {
// we only apply the proccessor if it is present
// that is due too CPU Usage ; This project was built for a DUAL Core CPU, not a quad or even 6 Core CPU
img = imageProcessor.apply(src);
}
processedImage = img;
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
);
g2d.drawImage(img, 0, 0, getWidth(), getHeight(), null);
}
public BufferedImage getCurrentProcessedImage() {
return processedImage;
}
}
}

View File

@@ -2,76 +2,84 @@ package io.swtc.proccessing;
import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamException;
import com.github.sarxos.webcam.WebcamResolution;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
import java.util.function.Consumer;
import javax.swing.*;
import io.swtc.proccessing.ui.ShowError;
public class WebcamCaptureLoop {
private final Webcam webcam;
private final Consumer<BufferedImage> onFrameCaptured;
private volatile boolean running = false;
private final AtomicBoolean cleanedUp = new AtomicBoolean(false);
private final int targetframes = 20;
public WebcamCaptureLoop(Webcam webcam, Consumer<BufferedImage> onFrameCaptured) {
this.webcam = webcam;
this.onFrameCaptured = onFrameCaptured;
// this is some of the most stupid shit ive seen in years, this is needed for opening the stream.
// the webcam package may not know the res before its opened and well this is before we open it. that is below in the thread.
// so this freaks out and doesnt want to cooperate.
if (!webcam.getName().toLowerCase().contains("ipcam")) {
Dimension[] sizes = webcam.getViewSizes();
webcam.setViewSize(sizes[sizes.length - 1]);
Dimension vga = WebcamResolution.VGA.getSize();
if (isSizeSupported(webcam, vga)) { // we dont do stupid shit anymore! cause we are smarter than before...
webcam.setViewSize(vga);
} else {
Dimension[] dimensions = webcam.getViewSizes();
webcam.setViewSize(dimensions[dimensions.length - 1]);
}
}
private boolean isSizeSupported(Webcam webcam, Dimension vga) {
for (Dimension d: webcam.getViewSizes()) {
if (
d.width == vga.width && d.height == vga.height
) return true; // this should return 640x480 :)
}
return false;
}
public void start() {
if (running) return;
running = true;
Thread captureThread = new Thread(() -> {
// this is where we open it. if the res isnt known then it fucks up.
try {
webcam.open();
} catch (WebcamException e) {
JOptionPane.showMessageDialog(
null,
"WebcamException" + e.getMessage(),
"WebcamException",
JOptionPane.ERROR_MESSAGE
);
}
if (!webcam.isOpen()) webcam.open(); // open if not
while (running) {
BufferedImage img = webcam.getImage();
if (img != null) {
//System.err.println(img);
onFrameCaptured.accept(img);
long frameDurationLimitns = 1_000_000_000L / targetframes; // we use NanoSeconds cause its more accurate
while (running) {
long startTime = System.nanoTime();
BufferedImage img = webcam.getImage();
if (img != null)
// there is no need for a invoke later swing wise!
onFrameCaptured.accept(img);
long timespent = System.nanoTime() - startTime;
long sleeptime = frameDurationLimitns - timespent;
if (sleeptime > 0) {
LockSupport.parkNanos(sleeptime);
} else {
// do a yield to prevent ui freezes
Thread.yield();
}
}
try {
Thread.sleep(15);
} catch (InterruptedException e) {
break;
}
}
try {
webcam.close();
} catch (IllegalStateException e) {
// show a error message from javax.swing.awt.JOptionPane
JOptionPane.showMessageDialog(
} catch (Exception e) {
ShowError.warning(
null,
"IllegalStateException@"+e,
"IllegalStateException",
JOptionPane.ERROR_MESSAGE
"Exception" + e,
"Exception"
);
} finally {
cleanup();
}
});
captureThread.setName("cam_cap_thread");
}, "cam_cap_thread");
captureThread.start();
}
@@ -111,6 +119,5 @@ public class WebcamCaptureLoop {
*/
public void stop() {
running = false;
cleanup();
}
}

View File

@@ -0,0 +1,38 @@
package io.swtc.proccessing.ui;
import javax.swing.*;
import java.awt.*;
public final class ShowError {
private ShowError() {
// we dont instantiate cause it causes some errors
}
public static void error(Component parent, String title, String message) {
JOptionPane.showMessageDialog(
parent,
message,
title,
JOptionPane.ERROR_MESSAGE
);
}
public static void warning(Component parent, String title, String message) {
JOptionPane.showMessageDialog(
parent,
message,
title,
JOptionPane.WARNING_MESSAGE
);
}
public static void info(Component parent, String title, String message) {
JOptionPane.showMessageDialog(
parent,
message,
title,
JOptionPane.INFORMATION_MESSAGE
);
}
}