zooming in CameraPanel.java with new methods and more efficient rendering!
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -45,4 +45,7 @@ dependency-reduced-pom.xml
|
|||||||
.idea
|
.idea
|
||||||
|
|
||||||
## This is for our app, cause it likes to store stuff ##
|
## This is for our app, cause it likes to store stuff ##
|
||||||
network_cameras.json
|
network_cameras.json
|
||||||
|
|
||||||
|
## exec launch4j config ##
|
||||||
|
execfg.xml
|
||||||
@@ -1,30 +1,106 @@
|
|||||||
package io.swtc.proccessing;
|
package io.swtc.proccessing;
|
||||||
|
|
||||||
|
import io.swtc.proccessing.ui.ShowError;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
|
||||||
* Enhanced CameraPanel with support for custom image processors
|
|
||||||
*/
|
|
||||||
public class CameraPanel extends JPanel {
|
public class CameraPanel extends JPanel {
|
||||||
|
|
||||||
private volatile BufferedImage sourceImage;
|
private volatile BufferedImage sourceImage;
|
||||||
private volatile BufferedImage processedImage;
|
private volatile BufferedImage processedImage;
|
||||||
|
|
||||||
private Function<BufferedImage, BufferedImage> imageProcessor;
|
private Function<BufferedImage, BufferedImage> imageProcessor;
|
||||||
private final AtomicBoolean repaintScheduled = new AtomicBoolean(false);
|
private final AtomicBoolean repaintScheduled = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private double zoomLevel = 1.0;
|
||||||
|
private double xOffset = 0;
|
||||||
|
private double yOffset = 0;
|
||||||
|
private Point dragStartPoint;
|
||||||
|
|
||||||
|
private static final double MIN_ZOOM = 1.0;
|
||||||
|
private static final double MAX_ZOOM = 10.0; // ten is enough if ur using 640x480
|
||||||
|
private static final double ZOOM_MULTIPLIER = 1.1;
|
||||||
|
|
||||||
public CameraPanel() {
|
public CameraPanel() {
|
||||||
setBackground(Color.BLACK);
|
setBackground(Color.BLACK);
|
||||||
setPreferredSize(new Dimension(640, 480));
|
setPreferredSize(new Dimension(640, 480));
|
||||||
|
|
||||||
|
initInteractionListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initInteractionListeners() {
|
||||||
|
MouseAdapter mouseHandler = new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseWheelMoved(MouseWheelEvent e) {
|
||||||
|
handleZoom(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
dragStartPoint = e.getPoint();
|
||||||
|
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (e.getClickCount() == 2)
|
||||||
|
resetView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e) { setCursor(Cursor.getDefaultCursor()); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
handlePan(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e) { setCursor(Cursor.getDefaultCursor()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
addMouseWheelListener(mouseHandler);
|
||||||
|
addMouseListener(mouseHandler);
|
||||||
|
addMouseMotionListener(mouseHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setImage(BufferedImage img) {
|
public void setImage(BufferedImage img) {
|
||||||
sourceImage = img;
|
sourceImage = img;
|
||||||
|
updateProcessedImage();
|
||||||
|
|
||||||
|
scheduleRepaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageProcessor(Function<BufferedImage, BufferedImage> processor) {
|
||||||
|
this.imageProcessor = processor;
|
||||||
|
if (sourceImage != null) {
|
||||||
|
updateProcessedImage();
|
||||||
|
scheduleRepaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProcessedImage() {
|
||||||
|
if (sourceImage == null) return;
|
||||||
|
|
||||||
|
if (imageProcessor != null) {
|
||||||
|
try {
|
||||||
|
processedImage = imageProcessor.apply(sourceImage);
|
||||||
|
} catch (Exception e) {
|
||||||
|
ShowError.error(null,"Fucked up in rendering \n" + Arrays.toString(e.getStackTrace()),"Problem");
|
||||||
|
processedImage = sourceImage; // Fallback
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
processedImage = sourceImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleRepaint() {
|
||||||
if (repaintScheduled.compareAndSet(false, true)) {
|
if (repaintScheduled.compareAndSet(false, true)) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
repaintScheduled.set(false);
|
repaintScheduled.set(false);
|
||||||
@@ -33,37 +109,100 @@ public class CameraPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setImageProcessor(Function<BufferedImage, BufferedImage> processor) {
|
private void handleZoom(MouseWheelEvent e) {
|
||||||
this.imageProcessor = processor;
|
if (processedImage == null) return;
|
||||||
|
|
||||||
|
double oldZoom = zoomLevel;
|
||||||
|
|
||||||
|
if (e.getWheelRotation() < 0) {
|
||||||
|
zoomLevel *= ZOOM_MULTIPLIER; // Zoom In
|
||||||
|
} else {
|
||||||
|
zoomLevel /= ZOOM_MULTIPLIER; // Zoom Out
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp shit
|
||||||
|
zoomLevel = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoomLevel));
|
||||||
|
|
||||||
|
if (oldZoom != zoomLevel) {
|
||||||
|
double xRel = e.getX() - xOffset;
|
||||||
|
double yRel = e.getY() - yOffset;
|
||||||
|
|
||||||
|
double zoomFactor = zoomLevel / oldZoom;
|
||||||
|
|
||||||
|
xOffset = e.getX() - (xRel * zoomFactor);
|
||||||
|
yOffset = e.getY() - (yRel * zoomFactor);
|
||||||
|
|
||||||
|
checkBounds();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handlePan(MouseEvent e) {
|
||||||
|
if (processedImage == null || dragStartPoint == null) return;
|
||||||
|
|
||||||
|
// Calculate delta
|
||||||
|
int dx = e.getX() - dragStartPoint.x;
|
||||||
|
int dy = e.getY() - dragStartPoint.y;
|
||||||
|
|
||||||
|
xOffset += dx;
|
||||||
|
yOffset += dy;
|
||||||
|
|
||||||
|
dragStartPoint = e.getPoint();
|
||||||
|
|
||||||
|
checkBounds();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBounds() {
|
||||||
|
double viewedWidth = getWidth() * zoomLevel;
|
||||||
|
double viewedHeight = getHeight() * zoomLevel;
|
||||||
|
|
||||||
|
if (xOffset > 0) xOffset = 0;
|
||||||
|
if (yOffset > 0) yOffset = 0;
|
||||||
|
|
||||||
|
if (xOffset + viewedWidth < getWidth()) {
|
||||||
|
xOffset = getWidth() - viewedWidth;
|
||||||
|
}
|
||||||
|
if (yOffset + viewedHeight < getHeight()) {
|
||||||
|
yOffset = getHeight() - viewedHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void paintComponent(Graphics g) {
|
protected void paintComponent(Graphics g) {
|
||||||
super.paintComponent(g);
|
super.paintComponent(g);
|
||||||
|
|
||||||
BufferedImage src = sourceImage;
|
if (processedImage == null) return;
|
||||||
if (src == null) return;
|
|
||||||
|
|
||||||
BufferedImage img = src;
|
|
||||||
|
|
||||||
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;
|
Graphics2D g2d = (Graphics2D) g;
|
||||||
|
|
||||||
|
AffineTransform originalTransform = g2d.getTransform();
|
||||||
|
|
||||||
g2d.setRenderingHint(
|
g2d.setRenderingHint(
|
||||||
RenderingHints.KEY_INTERPOLATION,
|
RenderingHints.KEY_INTERPOLATION,
|
||||||
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
|
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
|
||||||
);
|
);
|
||||||
|
|
||||||
g2d.drawImage(img, 0, 0, getWidth(), getHeight(), null);
|
g2d.translate(xOffset, yOffset);
|
||||||
|
|
||||||
|
double scaleX = (double) getWidth() / processedImage.getWidth();
|
||||||
|
double scaleY = (double) getHeight() / processedImage.getHeight();
|
||||||
|
|
||||||
|
g2d.scale(scaleX * zoomLevel, scaleY * zoomLevel);
|
||||||
|
|
||||||
|
g2d.drawImage(processedImage, 0, 0, null);
|
||||||
|
|
||||||
|
g2d.setTransform(originalTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage getCurrentProcessedImage() {
|
public BufferedImage getCurrentProcessedImage() {
|
||||||
return processedImage;
|
return processedImage;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public void resetView() {
|
||||||
|
zoomLevel = 1.0;
|
||||||
|
xOffset = 0;
|
||||||
|
yOffset = 0;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user