testing some new stuff!

This commit is contained in:
2026-01-18 21:00:24 +01:00
parent e7a3d98dd0
commit c393e05bb1
11 changed files with 917 additions and 415 deletions

View File

@@ -1,12 +1,21 @@
package io.swtc.proccessing;
/*
*
* Soon to be deprecated!
*
* */
/**
* Gray World Algorithm.
*
* <p>
* This class is an implementation of the Gray World algorithm, an automatic
* white balancing method.
* </p>
*
* <p>
* See:
* <a href="https://acorn.stanford.edu/psych221/projects/2000/trek/GWimages.html">
* Stanford explanation
* </a>
* </p>
*/
@Deprecated
public class AutoGainProcessor {
public float[] calculateAutoGains(int[] pixels) {

View File

@@ -3,16 +3,16 @@ package io.swtc.proccessing;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.function.Function;
/*
*
* Now here its getting rather interesting! this class processes some
* important stuff!
*
* */
/**
* Enhanced CameraPanel with support for custom image processors
*/
public class CameraPanel extends JPanel {
private BufferedImage currentImage;
private BufferedImage processedImage;
// Basic transform effects
private boolean mirror = false;
private boolean flip = false;
private boolean rotate = false;
@@ -21,150 +21,100 @@ public class CameraPanel extends JPanel {
private int brightness = 0;
private float contrast = 1.0f;
// Custom processor for advanced effects
private Function<BufferedImage, BufferedImage> imageProcessor;
public CameraPanel() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(640, 480));
}
public void setImage(BufferedImage img) {
this.currentImage = img;
this.repaint();
processImage();
repaint();
}
public BufferedImage getCurrentImage() {
return currentImage;
public void setImageProcessor(Function<BufferedImage, BufferedImage> processor) {
this.imageProcessor = processor;
processImage();
repaint();
}
public BufferedImage getCurrentProcessedImage() {
private void processImage() {
if (currentImage == null) {
return null;
processedImage = null;
return;
}
BufferedImage processed = currentImage;
BufferedImage result = currentImage;
// apply color effects
if (grayscale || invert || brightness != 0 || contrast != 1.0f) {
processed = applyColorEffects(processed);
// Apply basic transforms first
result = applyBasicEffects(result);
// Apply custom processor if set
if (imageProcessor != null) {
result = imageProcessor.apply(result);
}
// apply transform.
if (mirror || flip || rotate) {
processed = applyTransforms(processed);
}
return processed;
processedImage = result;
}
/* Helper Methods ... its the same boilerplate shit over and over again, im sick of this. */
public void setMirror(boolean mirror) {
this.mirror = mirror;
this.repaint();
}
private BufferedImage applyBasicEffects(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
public void setFlip(boolean flip) {
this.flip = flip;
this.repaint();
}
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// Handle mirror/flip/rotate
int srcX = x;
int srcY = y;
public void setRotate(boolean rotate) {
this.rotate = rotate;
this.repaint();
}
if (mirror) srcX = width - 1 - x;
if (flip) srcY = height - 1 - y;
if (rotate) {
int temp = srcX;
srcX = width - 1 - srcY;
srcY = temp;
}
public void setGrayscale(boolean grayscale) {
this.grayscale = grayscale;
this.repaint();
}
// Ensure coordinates are in bounds
srcX = Math.max(0, Math.min(width - 1, srcX));
srcY = Math.max(0, Math.min(height - 1, srcY));
public void setInvert(boolean invert) {
this.invert = invert;
this.repaint();
}
public void setBrightness(int brightness) {
this.brightness = brightness;
this.repaint();
}
public void setContrast(float contrast) {
this.contrast = contrast;
this.repaint();
}
public void resetEffects() {
mirror = flip = rotate = grayscale = invert = false;
brightness = 0;
contrast = 1.0f;
this.repaint();
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (currentImage != null) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
BufferedImage processedImage = currentImage;
// effects
if (grayscale || invert || brightness != 0 || contrast != 1.0f) {
processedImage = applyColorEffects(processedImage);
}
// transforms
int w = getWidth(), h = getHeight();
if (rotate) {
g2.translate(w / 2, h / 2);
g2.rotate(Math.PI);
g2.translate(-w / 2, -h / 2);
}
// here we have if, cause we need to do the stuff, yk?
if (mirror && flip) {
g2.drawImage(processedImage, w, h, -w, -h, null);
} else if (mirror) {
g2.drawImage(processedImage, w, 0, -w, h, null);
} else if (flip) {
g2.drawImage(processedImage, 0, h, w, -h, null);
} else {
g2.drawImage(processedImage, 0, 0, w, h, null);
}
}
}
private BufferedImage applyColorEffects(BufferedImage img) {
BufferedImage result = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < img.getHeight(); y++) {
for (int x = 0; x < img.getWidth(); x++) {
int rgb = img.getRGB(x, y);
int rgb = img.getRGB(srcX, srcY);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
// Apply grayscale
if (grayscale) {
int gray = (r + g + b) / 3;
r = g = b = gray;
}
// this is fun, this regulates the brightness or the contrast!
// this is some real java, the other stuff is just UI design, which i am bad at,
// but this! This is some real shit
r = (int) ((r - 128) * contrast + 128 + brightness);
g = (int) ((g - 128) * contrast + 128 + brightness);
b = (int) ((b - 128) * contrast + 128 + brightness);
// Apply brightness
if (brightness != 0) {
r = clamp(r + brightness);
g = clamp(g + brightness);
b = clamp(b + brightness);
}
// invert the colors!
// Apply contrast
if (contrast != 1.0f) {
r = clamp((int)((r - 128) * contrast + 128));
g = clamp((int)((g - 128) * contrast + 128));
b = clamp((int)((b - 128) * contrast + 128));
}
// Apply invert
if (invert) {
r = 255 - r;
g = 255 - g;
b = 255 - b;
}
// clamp so we dont get into weird color grades, or any weird looking spaces
// if we dont do this we cant really do stuff which looks bearable
r = Math.max(0, Math.min(255, r));
g = Math.max(0, Math.min(255, g));
b = Math.max(0, Math.min(255, b));
result.setRGB(x, y, (r << 16) | (g << 8) | b);
}
}
@@ -172,33 +122,95 @@ public class CameraPanel extends JPanel {
return result;
}
private BufferedImage applyTransforms(BufferedImage img) {
int width = img.getWidth();
int height = img.getHeight();
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
private int clamp(int value) {
return Math.max(0, Math.min(255, value));
}
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int sourceX = x;
int sourceY = y;
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (mirror) {
sourceX = width - 1 - x;
}
if (flip) {
sourceY = height - 1 - y;
}
if (rotate) {
int tempX = width - 1 - sourceX;
int tempY = height - 1 - sourceY;
sourceX = tempX;
sourceY = tempY;
}
if (processedImage != null) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
result.setRGB(x, y, img.getRGB(sourceX, sourceY));
}
// Scale image to fit panel while maintaining aspect ratio
int panelWidth = getWidth();
int panelHeight = getHeight();
int imgWidth = processedImage.getWidth();
int imgHeight = processedImage.getHeight();
double scaleX = (double) panelWidth / imgWidth;
double scaleY = (double) panelHeight / imgHeight;
double scale = Math.min(scaleX, scaleY);
int scaledWidth = (int) (imgWidth * scale);
int scaledHeight = (int) (imgHeight * scale);
int x = (panelWidth - scaledWidth) / 2;
int y = (panelHeight - scaledHeight) / 2;
g2d.drawImage(processedImage, x, y, scaledWidth, scaledHeight, null);
}
}
return result;
public BufferedImage getCurrentProcessedImage() {
return processedImage;
}
// Basic effect setters
public void setMirror(boolean mirror) {
this.mirror = mirror;
processImage();
repaint();
}
public void setFlip(boolean flip) {
this.flip = flip;
processImage();
repaint();
}
public void setRotate(boolean rotate) {
this.rotate = rotate;
processImage();
repaint();
}
public void setGrayscale(boolean grayscale) {
this.grayscale = grayscale;
processImage();
repaint();
}
public void setInvert(boolean invert) {
this.invert = invert;
processImage();
repaint();
}
public void setBrightness(int brightness) {
this.brightness = brightness;
processImage();
repaint();
}
public void setContrast(float contrast) {
this.contrast = contrast;
processImage();
repaint();
}
public void resetEffects() {
this.mirror = false;
this.flip = false;
this.rotate = false;
this.grayscale = false;
this.invert = false;
this.brightness = 0;
this.contrast = 1.0f;
this.imageProcessor = null;
processImage();
repaint();
}
}

View File

@@ -0,0 +1,81 @@
package io.swtc.proccessing;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.CubicCurve2D;
/**
* Acts as a transparent layer over the entire window to draw connections.
*/
public class ConnectionOverlay extends JComponent {
private final Component source;
private final Component target;
private final Color connectionColor;
public ConnectionOverlay(Component source, Component target, Color color) {
this.source = source;
this.target = target;
this.connectionColor = color;
setOpaque(false); // Make sure we can see through it
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (!source.isShowing() || !target.isShowing()) return;
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 1. Get absolute positions relative to this Overlay
Point p1 = SwingUtilities.convertPoint(source, 0, 0, this);
Point p2 = SwingUtilities.convertPoint(target, 0, 0, this);
int x1, y1, x2, y2, ctrl1X, ctrl2X;
// Calculate vertical centers
y1 = p1.y + (source.getHeight() / 2);
y2 = p2.y + (target.getHeight() / 2);
// 2. Logic to determine Left/Right orientation
// If source is to the left of target
if (p1.x + source.getWidth() < p2.x) {
x1 = p1.x + source.getWidth(); // Right edge of source
x2 = p2.x; // Left edge of target
}
// If source is to the right of target
else if (p1.x > p2.x + target.getWidth()) {
x1 = p1.x; // Left edge of source
x2 = p2.x + target.getWidth(); // Right edge of target
}
// If they are overlapping horizontally, use centers
else {
x1 = p1.x + (source.getWidth() / 2);
x2 = p2.x + (target.getWidth() / 2);
}
// 3. Dynamic Curve "Stiffness"
// The horizontal distance between the two points determines how far the curve pulls
int horizontalDist = Math.abs(x1 - x2);
int offset = Math.max(horizontalDist / 2, 20); // Minimum 20px pull for short distances
if (x1 < x2) {
ctrl1X = x1 + offset;
ctrl2X = x2 - offset;
} else {
ctrl1X = x1 - offset;
ctrl2X = x2 + offset;
}
// 4. Draw the Curve
g2d.setColor(connectionColor);
g2d.setStroke(new BasicStroke(3f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
// We keep the Y coordinates for controls the same as the endpoints
// to create that "horizontal entry/exit" look.
CubicCurve2D curve = new CubicCurve2D.Float(x1, y1, ctrl1X, y1, ctrl2X, y2, x2, y2);
g2d.draw(curve);
g2d.dispose();
}
}

View File

@@ -0,0 +1,25 @@
package io.swtc.proccessing;
public class EffectState {
public final boolean awbEnabled, dnrEnabled, edgeEnhance;
public final int awbStrength, dnrSpatial, dnrTemporal;
public final int temperature, tint, saturation, shadows, highlights, sharpness;
public EffectState(boolean awbEnabled, int awbStrength, boolean dnrEnabled,
int dnrSpatial, int dnrTemporal, int temperature, int tint,
int saturation, int shadows, int highlights,
int sharpness, boolean edgeEnhance) {
this.awbEnabled = awbEnabled;
this.awbStrength = awbStrength;
this.dnrEnabled = dnrEnabled;
this.dnrSpatial = dnrSpatial;
this.dnrTemporal = dnrTemporal;
this.temperature = temperature;
this.tint = tint;
this.saturation = saturation;
this.shadows = shadows;
this.highlights = highlights;
this.sharpness = sharpness;
this.edgeEnhance = edgeEnhance;
}
}

View File

@@ -0,0 +1,273 @@
package io.swtc.proccessing;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.image.BufferedImage;
import java.util.Random;
public class FilterPanel extends JPanel {
private final CameraPanel cameraPanel;
private final AutoGainProcessor autoGainProcessor;
private final PresetLibrary presetLibrary;
private final Color connectionColor;
private JCheckBox awbEnabled, dnrEnabled, edgeEnhance;
private JSlider awbStrength, dnrStrength, dnrTemporal, sharpness;
private JSlider saturation, temperature, tint, shadows, highlights;
private JComboBox<String> presetCombo;
private float[] currentGains = {1f, 1f, 1f};
public FilterPanel(CameraPanel cameraPanel) {
this.cameraPanel = cameraPanel;
this.autoGainProcessor = new AutoGainProcessor();
this.presetLibrary = new PresetLibrary();
// FIX: Randomize color with high saturation so it stands out
Random rand = new Random();
this.connectionColor = Color.getHSBColor(rand.nextFloat(), 0.9f, 0.9f);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder(new EmptyBorder(10, 10, 10, 10));
buildUI();
initListeners(); // Initialize anti-ghosting listeners
}
private void buildUI() {
add(createPresetPanel());
add(Box.createRigidArea(new Dimension(0, 10)));
JPanel container = new JPanel();
container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
container.add(createAWBPanel());
container.add(Box.createRigidArea(new Dimension(0, 10)));
container.add(createDNRPanel());
container.add(Box.createRigidArea(new Dimension(0, 10)));
container.add(createColorPanel());
container.add(Box.createRigidArea(new Dimension(0, 10)));
container.add(createDetailPanel());
JScrollPane scroll = new JScrollPane(container);
scroll.setBorder(null);
scroll.getVerticalScrollBar().setUnitIncrement(16);
add(scroll);
add(Box.createRigidArea(new Dimension(0, 10)));
add(createActionPanel());
}
public EffectState getCurrentState() {
return new EffectState(
awbEnabled.isSelected(), awbStrength.getValue(),
dnrEnabled.isSelected(), dnrStrength.getValue(), dnrTemporal.getValue(),
temperature.getValue(), tint.getValue(), saturation.getValue(),
shadows.getValue(), highlights.getValue(),
sharpness.getValue(), edgeEnhance.isSelected()
);
}
// Inside AdvancedEffectsPanel.java
private void initListeners() {
ComponentAdapter repaintListener = new ComponentAdapter() {
@Override
public void componentMoved(ComponentEvent e) {
updateOverlay();
}
@Override
public void componentResized(ComponentEvent e) {
updateOverlay();
}
};
this.addComponentListener(repaintListener);
if (this.cameraPanel != null) {
this.cameraPanel.addComponentListener(repaintListener);
}
}
private void updateOverlay() {
RootPaneContainer root = (RootPaneContainer) SwingUtilities.getWindowAncestor(this);
if (root != null) {
root.getGlassPane().repaint();
}
}
private void applyToCamera() {
EffectState state = getCurrentState();
cameraPanel.setImageProcessor(img ->
ImageEffectEngine.applyEffects(img, state, currentGains)
);
}
private JPanel createPresetPanel() {
JPanel panel = new JPanel(new BorderLayout(5, 5));
panel.setBorder(createTitledBorder("Presets"));
presetCombo = new JComboBox<>(new String[]{"Custom", "Natural", "Vivid", "Portrait", "Low Light", "Cinematic"});
JButton saveBtn = new JButton("Save");
presetCombo.addActionListener(e -> {
String selected = (String) presetCombo.getSelectedItem();
EffectState state = presetLibrary.get(selected);
if (state != null) applyPresetToUI(state);
});
saveBtn.addActionListener(e -> {
String name = JOptionPane.showInputDialog(this, "Preset Name:");
if (name != null) {
presetLibrary.savePreset(name, getCurrentState());
presetCombo.addItem(name);
}
});
panel.add(presetCombo, BorderLayout.CENTER);
panel.add(saveBtn, BorderLayout.EAST);
return panel;
}
private JPanel createDNRPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(createTitledBorder("3D Denoise (DNR)"));
dnrEnabled = new JCheckBox("Enable Temporal Denoise");
dnrStrength = addSliderToPanel(panel, "Spatial Strength", 0, 100, 30, "%");
dnrTemporal = addSliderToPanel(panel, "Temporal Strength", 0, 100, 50, "%");
dnrEnabled.addActionListener(e -> {
boolean enabled = dnrEnabled.isSelected();
dnrStrength.setEnabled(enabled);
dnrTemporal.setEnabled(enabled);
applyToCamera();
});
dnrStrength.setEnabled(false);
dnrTemporal.setEnabled(false);
panel.add(dnrEnabled, 0);
return panel;
}
private JPanel createDetailPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(createTitledBorder("Detail & Sharpness"));
sharpness = addSliderToPanel(panel, "Sharpness", 0, 200, 100, "%");
edgeEnhance = new JCheckBox("Edge Enhancement");
edgeEnhance.addActionListener(e -> applyToCamera());
panel.add(edgeEnhance);
return panel;
}
private JPanel createAWBPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(createTitledBorder("White Balance"));
awbEnabled = new JCheckBox("Enable AWB");
JPanel sPanel = createSliderPanel("Strength", 0, 100, 100, "%");
awbStrength = (JSlider) sPanel.getComponent(1);
JButton balanceBtn = new JButton("Balance Now");
awbEnabled.addActionListener(e -> {
boolean active = awbEnabled.isSelected();
awbStrength.setEnabled(active);
balanceBtn.setEnabled(active);
if (active) performOneTimeBalance();
else { currentGains = new float[]{1f, 1f, 1f}; applyToCamera(); }
});
awbStrength.addChangeListener(e -> { if(!awbStrength.getValueIsAdjusting()) applyToCamera(); });
balanceBtn.addActionListener(e -> performOneTimeBalance());
panel.add(awbEnabled);
panel.add(sPanel);
panel.add(balanceBtn);
return panel;
}
private JPanel createColorPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBorder(createTitledBorder("Color Grading"));
temperature = addSliderToPanel(panel, "Temperature", -100, 100, 0, "");
tint = addSliderToPanel(panel, "Tint", -100, 100, 0, "");
saturation = addSliderToPanel(panel, "Saturation", 0, 200, 100, "%");
shadows = addSliderToPanel(panel, "Shadows", -100, 100, 0, "");
highlights = addSliderToPanel(panel, "Highlights", -100, 100, 0, "");
return panel;
}
private void performOneTimeBalance() {
BufferedImage img = cameraPanel.getCurrentProcessedImage();
if (img != null) {
int[] pixels = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth());
currentGains = autoGainProcessor.calculateAutoGains(pixels);
applyToCamera();
}
}
private void applyPresetToUI(EffectState s) {
awbEnabled.setSelected(s.awbEnabled);
awbStrength.setValue(s.awbStrength);
dnrEnabled.setSelected(s.dnrEnabled);
dnrStrength.setValue(s.dnrSpatial);
temperature.setValue(s.temperature);
tint.setValue(s.tint);
saturation.setValue(s.saturation);
shadows.setValue(s.shadows);
highlights.setValue(s.highlights);
sharpness.setValue(s.sharpness);
edgeEnhance.setSelected(s.edgeEnhance);
applyToCamera();
}
private JSlider addSliderToPanel(JPanel parent, String label, int min, int max, int val, String unit) {
JPanel p = createSliderPanel(label, min, max, val, unit);
JSlider s = (JSlider) p.getComponent(1);
s.addChangeListener(e -> { if(!s.getValueIsAdjusting()) applyToCamera(); });
parent.add(p);
parent.add(Box.createRigidArea(new Dimension(0, 5)));
return s;
}
private JPanel createSliderPanel(String label, int min, int max, int initial, String unit) {
JPanel panel = new JPanel(new BorderLayout());
JLabel title = new JLabel(label + ": " + initial + unit);
JSlider slider = new JSlider(min, max, initial);
slider.addChangeListener(e -> title.setText(label + ": " + slider.getValue() + unit));
panel.add(title, BorderLayout.NORTH);
panel.add(slider, BorderLayout.CENTER);
return panel;
}
private TitledBorder createTitledBorder(String title) {
return BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), title);
}
private JPanel createActionPanel() {
JPanel panel = new JPanel(new GridLayout(1, 2, 10, 0));
JButton reset = new JButton("Reset All");
reset.addActionListener(e -> resetUI());
panel.add(reset);
return panel;
}
private void resetUI() {
applyPresetToUI(new EffectState(false, 100, false, 30, 50, 0, 0, 100, 0, 0, 100, false));
}
}

View File

@@ -0,0 +1,147 @@
package io.swtc.proccessing;
import java.awt.image.BufferedImage;
public class ImageEffectEngine {
public static BufferedImage applyEffects(BufferedImage img, EffectState state, float[] currentGains) {
if (img == null) return null;
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++) {
int rgb = img.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
// 1. AWB
if (state.awbEnabled) {
float s = state.awbStrength / 100f;
r = (int) Math.min(255, r * (1 + (currentGains[0] - 1) * s));
g = (int) Math.min(255, g * (1 + (currentGains[1] - 1) * s));
b = (int) Math.min(255, b * (1 + (currentGains[2] - 1) * s));
}
// 2. Temp & Tint
if (state.temperature != 0) {
float factor = state.temperature / 100f;
r = clamp(r + (int)(factor * 30));
b = clamp(b - (int)(factor * 30));
}
if (state.tint != 0) {
g = clamp(g + (int)((state.tint / 100f) * 20));
}
// 3. Saturation
if (state.saturation != 100) {
float factor = state.saturation / 100f;
float gray = (r + g + b) / 3f;
r = clamp((int)(gray + (r - gray) * factor));
g = clamp((int)(gray + (g - gray) * factor));
b = clamp((int)(gray + (b - gray) * factor));
}
// 4. Shadows/Highlights
float lum = (r + g + b) / 3f / 255f;
if (lum < 0.5f && state.shadows != 0) {
int adj = (int)((state.shadows / 100f) * (1 - lum * 2) * 50);
r = clamp(r + adj); g = clamp(g + adj); b = clamp(b + adj);
} else if (lum > 0.5f && state.highlights != 0) {
int adj = (int)((state.highlights / 100f) * (lum * 2 - 1) * 50);
r = clamp(r + adj); g = clamp(g + adj); b = clamp(b + adj);
}
result.setRGB(x, y, (r << 16) | (g << 8) | b);
}
}
if (state.sharpness != 100 || state.edgeEnhance) {
result = applySharpness(result, state.sharpness / 100f, state.edgeEnhance);
}
if (state.dnrEnabled) {
result = applyDenoise(result, state.dnrSpatial / 100f);
}
return result;
}
private static BufferedImage applySharpness(BufferedImage img, float amount, boolean edgeEnhance) {
if (amount == 1f && !edgeEnhance) return img;
int width = img.getWidth();
int height = img.getHeight();
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
float[][] kernel = edgeEnhance ?
new float[][]{{0, -1, 0}, {-1, 5, -1}, {0, -1, 0}} :
new float[][]{{-1, -1, -1}, {-1, 9, -1}, {-1, -1, -1}};
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
float r = 0, g = 0, b = 0;
for (int ky = -1; ky <= 1; ky++) {
for (int kx = -1; kx <= 1; kx++) {
int rgb = img.getRGB(x + kx, y + ky);
float weight = kernel[ky + 1][kx + 1] * (amount - 1) / 8f;
if (kx == 0 && ky == 0) weight += 1;
r += ((rgb >> 16) & 0xFF) * weight;
g += ((rgb >> 8) & 0xFF) * weight;
b += (rgb & 0xFF) * weight;
}
}
result.setRGB(x, y, (clamp((int)r) << 16) | (clamp((int)g) << 8) | clamp((int)b));
}
}
return result;
}
private static BufferedImage applyDenoise(BufferedImage img, float strength) {
if (strength == 0) return img;
int width = img.getWidth();
int height = img.getHeight();
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int radius = (int)(strength * 2) + 1;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rSum = 0, gSum = 0, bSum = 0, count = 0;
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
int nx = Math.min(width - 1, Math.max(0, x + dx));
int ny = Math.min(height - 1, Math.max(0, y + dy));
int rgb = img.getRGB(nx, ny);
rSum += (rgb >> 16) & 0xFF;
gSum += (rgb >> 8) & 0xFF;
bSum += rgb & 0xFF;
count++;
}
}
int r = rSum / count;
int g = gSum / count;
int b = bSum / count;
result.setRGB(x, y, (r << 16) | (g << 8) | b);
}
}
return result;
}
private static int clamp(int val) {
return Math.max(0, Math.min(255, val));
}
}

View File

@@ -0,0 +1,46 @@
package io.swtc.proccessing;
import java.util.HashMap;
import java.util.Map;
public class PresetLibrary {
private final Map<String, EffectState> presets = new HashMap<>();
public PresetLibrary() {
presets.put("Natural", new EffectState(
false, 100, false, 20, 40, 0, 0, 100, 0, 0, 100, false
));
presets.put("Vivid", new EffectState(
true, 100, false, 15, 30, 10, 5, 130, 0, 0, 120, true
));
presets.put("Portrait", new EffectState(
true, 80, true, 40, 50, -5, 10, 95, 10, -5, 90, false
));
presets.put("Low Light", new EffectState(
true, 100, true, 60, 70, 0, 0, 110, 20, -10, 80, false
));
presets.put("High Contrast", new EffectState(
false, 100, false, 25, 35, 0, 0, 120, -20, 20, 130, true
));
presets.put("Cinematic", new EffectState(
true, 70, true, 30, 45, -15, -5, 90, -10, 5, 110, false
));
}
public void savePreset(String name, EffectState state) {
presets.put(name, state);
}
public EffectState get(String name) {
return presets.get(name);
}
public String[] getPresetNames() {
return presets.keySet().toArray(new String[0]);
}
}